Beispiel #1
0
 def produce_into(self, builder, r):
     v, A, v_index, descr = self.field_descr(builder, r)
     while True:
         if r.random() < 0.3:
             w = ConstInt(r.random_integer())
         else:
             w = r.choice(builder.intvars)
         value = w.getint()
         if rffi.cast(lltype.Signed, rffi.cast(A.OF, value)) == value:
             break
     builder.do(self.opnum, [v, v_index, w], descr)
Beispiel #2
0
 def gen_guard(self, builder, r):
     v = r.choice(builder.intvars)
     if r.random() > 0.8:
         other = r.choice(builder.intvars)
     else:
         if r.random() < 0.75:
             value = v.getint()
         elif r.random() < 0.5:
             value = v.getint() ^ 1
         else:
             value = r.random_integer()
         other = ConstInt(value)
     op = ResOperation(self.opnum, [v, other])
     return op, (v.getint() == other.getint())
Beispiel #3
0
    def test_replace_box_with_const_in_array(self):
        h = HeapCache()
        box1 = RefFrontendOp(1)
        lengthbox2 = IntFrontendOp(2)
        lengthbox2.setint(10)
        h.arraylen_now_known(box1, lengthbox2)
        assert h.arraylen(box1) is lengthbox2
        c10 = ConstInt(10)
        h.replace_box(lengthbox2, c10)
        assert c10.same_constant(h.arraylen(box1))

        box2 = IntFrontendOp(2)
        box2.setint(12)
        h.setarrayitem(box1, index2, box2, descr1)
        assert h.getarrayitem(box1, index2, descr1) is box2
        c12 = ConstInt(12)
        h.replace_box(box2, c12)
        assert c12.same_constant(h.getarrayitem(box1, index2, descr1))
Beispiel #4
0
class GcRewriterAssembler(object):
    """ This class performs the following rewrites on the list of operations:

     - Turn all NEW_xxx to either a CALL_R/CHECK_MEMORY_ERROR,
       or a CALL_MALLOC_NURSERY,
       followed by SETFIELDs in order to initialize their GC fields.  The
       two advantages of CALL_MALLOC_NURSERY is that it inlines the common
       path, and we need only one such operation to allocate several blocks
       of memory at once.

     - Add COND_CALLs to the write barrier before SETFIELD_GC and
       SETARRAYITEM_GC operations.

    '_write_barrier_applied' contains a dictionary of variable -> None.
    If a variable is in the dictionary, next setfields can be called without
    a write barrier.  The idea is that an object that was freshly allocated
    or already write_barrier'd don't need another write_barrier if there
    was no potentially collecting resop inbetween.
    """

    _previous_size = -1
    _op_malloc_nursery = None
    _v_last_malloced_nursery = None
    c_zero = ConstInt(0)
    c_null = ConstPtr(lltype.nullptr(llmemory.GCREF.TO))

    def __init__(self, gc_ll_descr, cpu):
        self.gc_ll_descr = gc_ll_descr
        self.cpu = cpu
        self._newops = []
        self._known_lengths = {}
        self._write_barrier_applied = {}
        self._delayed_zero_setfields = {}
        self.last_zero_arrays = []
        self._setarrayitems_occurred = {}  # {box: {set-of-indexes}}

    def remember_known_length(self, op, val):
        self._known_lengths[op] = val

    def remember_setarrayitem_occured(self, op, index):
        op = self.get_box_replacement(op)
        try:
            subs = self._setarrayitems_occurred[op]
        except KeyError:
            subs = {}
            self._setarrayitems_occurred[op] = subs
        subs[index] = None

    def setarrayitems_occurred(self, op):
        return self._setarrayitems_occurred[self.get_box_replacement(op)]

    def known_length(self, op, default):
        return self._known_lengths.get(op, default)

    def delayed_zero_setfields(self, op):
        op = self.get_box_replacement(op)
        try:
            d = self._delayed_zero_setfields[op]
        except KeyError:
            d = {}
            self._delayed_zero_setfields[op] = d
        return d

    def get_box_replacement(self, op, allow_none=False):
        if allow_none and op is None:
            return None  # for failargs
        while op.get_forwarded():
            op = op.get_forwarded()
        return op

    def emit_op(self, op):
        op = self.get_box_replacement(op)
        orig_op = op
        replaced = False
        opnum = op.getopnum()
        keep = (opnum == rop.JIT_DEBUG)
        for i in range(op.numargs()):
            orig_arg = op.getarg(i)
            arg = self.get_box_replacement(orig_arg)
            if isinstance(arg, ConstPtr) and bool(arg.value) and not keep:
                arg = self.remove_constptr(arg)
            if orig_arg is not arg:
                if not replaced:
                    op = op.copy_and_change(opnum)
                    orig_op.set_forwarded(op)
                    replaced = True
                op.setarg(i, arg)
        if rop.is_guard(opnum):
            if not replaced:
                op = op.copy_and_change(opnum)
                orig_op.set_forwarded(op)
            op.setfailargs(
                [self.get_box_replacement(a, True) for a in op.getfailargs()])
        if rop.is_guard(opnum) or opnum == rop.FINISH:
            llref = cast_instance_to_gcref(op.getdescr())
            self.gcrefs_output_list.append(llref)
        self._newops.append(op)

    def replace_op_with(self, op, newop):
        assert not op.get_forwarded()
        op.set_forwarded(newop)

    def handle_setarrayitem(self, op):
        itemsize, basesize, _ = unpack_arraydescr(op.getdescr())
        ptr_box = op.getarg(0)
        index_box = op.getarg(1)
        value_box = op.getarg(2)
        self.emit_gc_store_or_indexed(op, ptr_box, index_box, value_box,
                                      itemsize, itemsize, basesize)

    def emit_gc_store_or_indexed(self, op, ptr_box, index_box, value_box,
                                 itemsize, factor, offset):
        factor, offset, index_box = \
                self._emit_mul_if_factor_offset_not_supported(index_box,
                        factor, offset)
        #
        if index_box is None:
            args = [ptr_box, ConstInt(offset), value_box, ConstInt(itemsize)]
            newload = ResOperation(rop.GC_STORE, args)
        else:
            args = [
                ptr_box, index_box, value_box,
                ConstInt(factor),
                ConstInt(offset),
                ConstInt(itemsize)
            ]
            newload = ResOperation(rop.GC_STORE_INDEXED, args)
        if op is not None:
            self.replace_op_with(op, newload)
        else:
            self.emit_op(newload)

    def handle_getarrayitem(self, op):
        itemsize, ofs, sign = unpack_arraydescr(op.getdescr())
        ptr_box = op.getarg(0)
        index_box = op.getarg(1)
        self.emit_gc_load_or_indexed(op, ptr_box, index_box, itemsize,
                                     itemsize, ofs, sign)

    def _emit_mul_if_factor_offset_not_supported(self, index_box, factor,
                                                 offset):
        factor, offset, new_index_box, emit = cpu_simplify_scale(
            self.cpu, index_box, factor, offset)
        if emit:
            self.emit_op(new_index_box)
        return factor, offset, new_index_box

    def emit_gc_load_or_indexed(self,
                                op,
                                ptr_box,
                                index_box,
                                itemsize,
                                factor,
                                offset,
                                sign,
                                type='i'):
        factor, offset, index_box = \
                self._emit_mul_if_factor_offset_not_supported(index_box,
                        factor, offset)
        #
        if sign:
            # encode signed into the itemsize value
            itemsize = -itemsize
        #
        optype = type
        if op is not None:
            optype = op.type
        if index_box is None:
            args = [ptr_box, ConstInt(offset), ConstInt(itemsize)]
            newload = ResOperation(OpHelpers.get_gc_load(optype), args)
        else:
            args = [
                ptr_box, index_box,
                ConstInt(factor),
                ConstInt(offset),
                ConstInt(itemsize)
            ]
            newload = ResOperation(OpHelpers.get_gc_load_indexed(optype), args)
        if op is None:
            self.emit_op(newload)
        else:
            self.replace_op_with(op, newload)
        return newload

    def transform_to_gc_load(self, op):
        NOT_SIGNED = 0
        CINT_ZERO = ConstInt(0)
        opnum = op.getopnum()
        if rop.is_getarrayitem(opnum) or \
           opnum in (rop.GETARRAYITEM_RAW_I,
                     rop.GETARRAYITEM_RAW_F):
            self.handle_getarrayitem(op)
        elif opnum in (rop.SETARRAYITEM_GC, rop.SETARRAYITEM_RAW):
            self.handle_setarrayitem(op)
        elif opnum == rop.RAW_STORE:
            itemsize, ofs, _ = unpack_arraydescr(op.getdescr())
            ptr_box = op.getarg(0)
            index_box = op.getarg(1)
            value_box = op.getarg(2)
            self.emit_gc_store_or_indexed(op, ptr_box, index_box, value_box,
                                          itemsize, 1, ofs)
        elif opnum in (rop.RAW_LOAD_I, rop.RAW_LOAD_F):
            itemsize, ofs, sign = unpack_arraydescr(op.getdescr())
            ptr_box = op.getarg(0)
            index_box = op.getarg(1)
            self.emit_gc_load_or_indexed(op, ptr_box, index_box, itemsize, 1,
                                         ofs, sign)
        elif opnum in (rop.GETINTERIORFIELD_GC_I, rop.GETINTERIORFIELD_GC_R,
                       rop.GETINTERIORFIELD_GC_F):
            ofs, itemsize, fieldsize, sign = unpack_interiorfielddescr(
                op.getdescr())
            ptr_box = op.getarg(0)
            index_box = op.getarg(1)
            self.emit_gc_load_or_indexed(op, ptr_box, index_box, fieldsize,
                                         itemsize, ofs, sign)
        elif opnum in (rop.SETINTERIORFIELD_RAW, rop.SETINTERIORFIELD_GC):
            ofs, itemsize, fieldsize, sign = unpack_interiorfielddescr(
                op.getdescr())
            ptr_box = op.getarg(0)
            index_box = op.getarg(1)
            value_box = op.getarg(2)
            self.emit_gc_store_or_indexed(op, ptr_box, index_box, value_box,
                                          fieldsize, itemsize, ofs)
        elif opnum in (rop.GETFIELD_GC_I, rop.GETFIELD_GC_F, rop.GETFIELD_GC_R,
                       rop.GETFIELD_RAW_I, rop.GETFIELD_RAW_F,
                       rop.GETFIELD_RAW_R):
            ofs, itemsize, sign = unpack_fielddescr(op.getdescr())
            ptr_box = op.getarg(0)
            if op.getopnum() in (rop.GETFIELD_GC_F, rop.GETFIELD_GC_I,
                                 rop.GETFIELD_GC_R):
                # See test_zero_ptr_field_before_getfield().  We hope there is
                # no getfield_gc in the middle of initialization code, but there
                # shouldn't be, given that a 'new' is already delayed by previous
                # optimization steps.  In practice it should immediately be
                # followed by a bunch of 'setfields', and the 'pending_zeros'
                # optimization we do here is meant for this case.
                self.emit_pending_zeros()
                self.emit_gc_load_or_indexed(op, ptr_box, ConstInt(0),
                                             itemsize, 1, ofs, sign)
                self.emit_op(op)
                return True
            self.emit_gc_load_or_indexed(op, ptr_box, ConstInt(0), itemsize, 1,
                                         ofs, sign)
        elif opnum in (rop.SETFIELD_GC, rop.SETFIELD_RAW):
            ofs, itemsize, sign = unpack_fielddescr(op.getdescr())
            ptr_box = op.getarg(0)
            value_box = op.getarg(1)
            self.emit_gc_store_or_indexed(op, ptr_box, ConstInt(0), value_box,
                                          itemsize, 1, ofs)
        elif opnum == rop.ARRAYLEN_GC:
            descr = op.getdescr()
            assert isinstance(descr, ArrayDescr)
            ofs = descr.lendescr.offset
            self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD,
                                         1, ofs, NOT_SIGNED)
        elif opnum == rop.STRLEN:
            basesize, itemsize, ofs_length = get_array_token(
                rstr.STR, self.cpu.translate_support_code)
            self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD,
                                         1, ofs_length, NOT_SIGNED)
        elif opnum == rop.UNICODELEN:
            basesize, itemsize, ofs_length = get_array_token(
                rstr.UNICODE, self.cpu.translate_support_code)
            self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD,
                                         1, ofs_length, NOT_SIGNED)
        elif opnum == rop.STRHASH:
            offset, size = get_field_token(rstr.STR, 'hash',
                                           self.cpu.translate_support_code)
            assert size == WORD
            self.emit_gc_load_or_indexed(op,
                                         op.getarg(0),
                                         ConstInt(0),
                                         WORD,
                                         1,
                                         offset,
                                         sign=True)
        elif opnum == rop.UNICODEHASH:
            offset, size = get_field_token(rstr.UNICODE, 'hash',
                                           self.cpu.translate_support_code)
            assert size == WORD
            self.emit_gc_load_or_indexed(op,
                                         op.getarg(0),
                                         ConstInt(0),
                                         WORD,
                                         1,
                                         offset,
                                         sign=True)
        elif opnum == rop.STRGETITEM:
            basesize, itemsize, ofs_length = get_array_token(
                rstr.STR, self.cpu.translate_support_code)
            assert itemsize == 1
            basesize -= 1  # for the extra null character
            self.emit_gc_load_or_indexed(op, op.getarg(0), op.getarg(1),
                                         itemsize, itemsize, basesize,
                                         NOT_SIGNED)
        elif opnum == rop.UNICODEGETITEM:
            basesize, itemsize, ofs_length = get_array_token(
                rstr.UNICODE, self.cpu.translate_support_code)
            self.emit_gc_load_or_indexed(op, op.getarg(0), op.getarg(1),
                                         itemsize, itemsize, basesize,
                                         NOT_SIGNED)
        elif opnum == rop.STRSETITEM:
            basesize, itemsize, ofs_length = get_array_token(
                rstr.STR, self.cpu.translate_support_code)
            assert itemsize == 1
            basesize -= 1  # for the extra null character
            self.emit_gc_store_or_indexed(op, op.getarg(0), op.getarg(1),
                                          op.getarg(2), itemsize, itemsize,
                                          basesize)
        elif opnum == rop.UNICODESETITEM:
            basesize, itemsize, ofs_length = get_array_token(
                rstr.UNICODE, self.cpu.translate_support_code)
            self.emit_gc_store_or_indexed(op, op.getarg(0), op.getarg(1),
                                          op.getarg(2), itemsize, itemsize,
                                          basesize)
        return False

    def rewrite(self, operations, gcrefs_output_list):
        # we can only remember one malloc since the next malloc can possibly
        # collect; but we can try to collapse several known-size mallocs into
        # one, both for performance and to reduce the number of write
        # barriers.  We do this on each "basic block" of operations, which in
        # this case means between CALLs or unknown-size mallocs.
        #
        self.gcrefs_output_list = gcrefs_output_list
        self.gcrefs_map = None
        self.gcrefs_recently_loaded = None
        operations = self.remove_bridge_exception(operations)
        self._changed_op = None
        for i in range(len(operations)):
            op = operations[i]
            if op.get_forwarded():
                msg = '[rewrite] operations at %d has forwarded info %s\n' % (
                    i, op.repr({}))
                if we_are_translated():
                    llop.debug_print(lltype.Void, msg)
                raise NotImplementedError(msg)
            if op.getopnum() == rop.DEBUG_MERGE_POINT:
                continue
            if op is self._changed_op:
                op = self._changed_op_to
            # ---------- GC_LOAD/STORE transformations --------------
            if self.transform_to_gc_load(op):
                continue
            # ---------- turn NEWxxx into CALL_MALLOC_xxx ----------
            if rop.is_malloc(op.opnum):
                self.handle_malloc_operation(op)
                continue
            if (rop.is_guard(op.opnum)
                    or self.could_merge_with_next_guard(op, i, operations)):
                self.emit_pending_zeros()
            elif rop.can_malloc(op.opnum):
                self.emitting_an_operation_that_can_collect()
            elif op.getopnum() == rop.LABEL:
                self.emit_label()
            # ---------- write barriers ----------
            if self.gc_ll_descr.write_barrier_descr is not None:
                if op.getopnum() == rop.SETFIELD_GC:
                    self.consider_setfield_gc(op)
                    self.handle_write_barrier_setfield(op)
                    continue
                if op.getopnum() == rop.SETINTERIORFIELD_GC:
                    self.handle_write_barrier_setinteriorfield(op)
                    continue
                if op.getopnum() == rop.SETARRAYITEM_GC:
                    self.consider_setarrayitem_gc(op)
                    self.handle_write_barrier_setarrayitem(op)
                    continue
            else:
                # this is dead code, but in case we have a gc that does
                # not have a write barrier and does not zero memory, we would
                # need to call it
                if op.getopnum() == rop.SETFIELD_GC:
                    self.consider_setfield_gc(op)
                elif op.getopnum() == rop.SETARRAYITEM_GC:
                    self.consider_setarrayitem_gc(op)
            # ---------- call assembler -----------
            if OpHelpers.is_call_assembler(op.getopnum()):
                self.handle_call_assembler(op)
                continue
            if op.getopnum() == rop.JUMP or op.getopnum() == rop.FINISH:
                self.emit_pending_zeros()
            #
            self.emit_op(op)
        return self._newops

    def could_merge_with_next_guard(self, op, i, operations):
        # return True in cases where the operation and the following guard
        # should likely remain together.  Simplified version of
        # can_merge_with_next_guard() in llsupport/regalloc.py.
        if not rop.is_comparison(op.opnum):
            return rop.is_ovf(op.opnum)  # int_xxx_ovf() / guard_no_overflow()
        if i + 1 >= len(operations):
            return False
        next_op = operations[i + 1]
        opnum = next_op.getopnum()
        if not (opnum == rop.GUARD_TRUE or opnum == rop.GUARD_FALSE
                or opnum == rop.COND_CALL):
            return False
        if next_op.getarg(0) is not op:
            return False
        self.remove_tested_failarg(next_op)
        return True

    def remove_tested_failarg(self, op):
        opnum = op.getopnum()
        if not (opnum == rop.GUARD_TRUE or opnum == rop.GUARD_FALSE):
            return
        if op.getarg(0).is_vector():
            return
        try:
            i = op.getfailargs().index(op.getarg(0))
        except ValueError:
            return
        # The checked value is also in the failargs.  The front-end
        # tries not to produce it, but doesn't always succeed (and
        # it's hard to test all cases).  Rewrite it away.
        value = int(opnum == rop.GUARD_FALSE)
        op1 = ResOperation(rop.SAME_AS_I, [ConstInt(value)])
        self.emit_op(op1)
        lst = op.getfailargs()[:]
        lst[i] = op1
        newop = op.copy_and_change(opnum)
        newop.setfailargs(lst)
        self._changed_op = op
        self._changed_op_to = newop

    # ----------

    def handle_malloc_operation(self, op):
        opnum = op.getopnum()
        if opnum == rop.NEW:
            self.handle_new_fixedsize(op.getdescr(), op)
        elif opnum == rop.NEW_WITH_VTABLE:
            descr = op.getdescr()
            self.handle_new_fixedsize(descr, op)
            if self.gc_ll_descr.fielddescr_vtable is not None:
                self.emit_setfield(op,
                                   ConstInt(descr.get_vtable()),
                                   descr=self.gc_ll_descr.fielddescr_vtable)
        elif opnum == rop.NEW_ARRAY or opnum == rop.NEW_ARRAY_CLEAR:
            descr = op.getdescr()
            assert isinstance(descr, ArrayDescr)
            self.handle_new_array(descr, op)
        elif opnum == rop.NEWSTR:
            self.handle_new_array(self.gc_ll_descr.str_descr,
                                  op,
                                  kind=FLAG_STR)
        elif opnum == rop.NEWUNICODE:
            self.handle_new_array(self.gc_ll_descr.unicode_descr,
                                  op,
                                  kind=FLAG_UNICODE)
        else:
            raise NotImplementedError(op.getopname())

    def clear_gc_fields(self, descr, result):
        if self.gc_ll_descr.malloc_zero_filled:
            return
        d = self.delayed_zero_setfields(result)
        for fielddescr in descr.gc_fielddescrs:
            ofs = self.cpu.unpack_fielddescr(fielddescr)
            d[ofs] = None

    def consider_setfield_gc(self, op):
        offset = self.cpu.unpack_fielddescr(op.getdescr())
        try:
            del self._delayed_zero_setfields[self.get_box_replacement(
                op.getarg(0))][offset]
        except KeyError:
            pass

    def consider_setarrayitem_gc(self, op):
        array_box = op.getarg(0)
        index_box = op.getarg(1)
        if not isinstance(array_box, ConstPtr) and index_box.is_constant():
            self.remember_setarrayitem_occured(array_box, index_box.getint())

    def clear_varsize_gc_fields(self, kind, descr, result, v_length, opnum):
        if self.gc_ll_descr.malloc_zero_filled:
            return
        if kind == FLAG_ARRAY:
            if descr.is_array_of_structs() or descr.is_array_of_pointers():
                assert opnum == rop.NEW_ARRAY_CLEAR
            if opnum == rop.NEW_ARRAY_CLEAR:
                self.handle_clear_array_contents(descr, result, v_length)
            return
        if kind == FLAG_STR:
            hash_descr = self.gc_ll_descr.str_hash_descr
        elif kind == FLAG_UNICODE:
            hash_descr = self.gc_ll_descr.unicode_hash_descr
        else:
            return
        self.emit_setfield(result, self.c_zero, descr=hash_descr)

    def handle_new_fixedsize(self, descr, op):
        assert isinstance(descr, SizeDescr)
        size = descr.size
        if self.gen_malloc_nursery(size, op):
            self.gen_initialize_tid(op, descr.tid)
        else:
            self.gen_malloc_fixedsize(size, descr.tid, op)
        self.clear_gc_fields(descr, op)

    def handle_new_array(self, arraydescr, op, kind=FLAG_ARRAY):
        v_length = self.get_box_replacement(op.getarg(0))
        total_size = -1
        if isinstance(v_length, ConstInt):
            num_elem = v_length.getint()
            self.remember_known_length(op, num_elem)
            try:
                var_size = ovfcheck(arraydescr.itemsize * num_elem)
                total_size = ovfcheck(arraydescr.basesize + var_size)
            except OverflowError:
                pass  # total_size is still -1
        elif arraydescr.itemsize == 0:
            total_size = arraydescr.basesize
        elif (self.gc_ll_descr.can_use_nursery_malloc(1)
              and self.gen_malloc_nursery_varsize(
                  arraydescr.itemsize, v_length, op, arraydescr, kind=kind)):
            # note that we cannot initialize tid here, because the array
            # might end up being allocated by malloc_external or some
            # stuff that initializes GC header fields differently
            self.gen_initialize_len(op, v_length, arraydescr.lendescr)
            self.clear_varsize_gc_fields(kind, op.getdescr(), op, v_length,
                                         op.getopnum())
            return
        if (total_size >= 0 and self.gen_malloc_nursery(total_size, op)):
            self.gen_initialize_tid(op, arraydescr.tid)
            self.gen_initialize_len(op, v_length, arraydescr.lendescr)
        elif self.gc_ll_descr.kind == 'boehm':
            self.gen_boehm_malloc_array(arraydescr, v_length, op)
        else:
            opnum = op.getopnum()
            if opnum == rop.NEW_ARRAY or opnum == rop.NEW_ARRAY_CLEAR:
                self.gen_malloc_array(arraydescr, v_length, op)
            elif opnum == rop.NEWSTR:
                self.gen_malloc_str(v_length, op)
            elif opnum == rop.NEWUNICODE:
                self.gen_malloc_unicode(v_length, op)
            else:
                raise NotImplementedError(op.getopname())
        self.clear_varsize_gc_fields(kind, op.getdescr(), op, v_length,
                                     op.getopnum())

    def handle_clear_array_contents(self, arraydescr, v_arr, v_length):
        assert v_length is not None
        if isinstance(v_length, ConstInt) and v_length.getint() == 0:
            return
        # the ZERO_ARRAY operation will be optimized according to what
        # SETARRAYITEM_GC we see before the next allocation operation.
        # See emit_pending_zeros().  (This optimization is done by
        # hacking the object 'o' in-place: e.g., o.getarg(1) may be
        # replaced with another constant greater than 0.)
        assert isinstance(arraydescr, ArrayDescr)
        scale = arraydescr.itemsize
        v_length_scaled = v_length
        if not isinstance(v_length, ConstInt):
            scale, offset, v_length_scaled = \
                    self._emit_mul_if_factor_offset_not_supported(v_length, scale, 0)
        v_scale = ConstInt(scale)
        # there is probably no point in doing _emit_mul_if.. for c_zero!
        # NOTE that the scale might be != 1 for e.g. v_length_scaled if it is a constant
        # it is later applied in emit_pending_zeros
        args = [v_arr, self.c_zero, v_length_scaled, ConstInt(scale), v_scale]
        o = ResOperation(rop.ZERO_ARRAY, args, descr=arraydescr)
        self.emit_op(o)
        if isinstance(v_length, ConstInt):
            self.last_zero_arrays.append(self._newops[-1])

    def gen_malloc_frame(self, frame_info):
        descrs = self.gc_ll_descr.getframedescrs(self.cpu)
        if self.gc_ll_descr.kind == 'boehm':
            ofs, size, sign = unpack_fielddescr(descrs.jfi_frame_depth)
            if sign:
                size = -size
            args = [ConstInt(frame_info), ConstInt(ofs), ConstInt(size)]
            size = ResOperation(rop.GC_LOAD_I, args)
            self.emit_op(size)
            frame = ResOperation(rop.NEW_ARRAY, [size],
                                 descr=descrs.arraydescr)
            self.handle_new_array(descrs.arraydescr, frame)
            return self.get_box_replacement(frame)
        else:
            # we read size in bytes here, not the length
            ofs, size, sign = unpack_fielddescr(descrs.jfi_frame_size)
            if sign:
                size = -size
            args = [ConstInt(frame_info), ConstInt(ofs), ConstInt(size)]
            size = ResOperation(rop.GC_LOAD_I, args)
            self.emit_op(size)
            frame = self.gen_malloc_nursery_varsize_frame(size)
            self.gen_initialize_tid(frame, descrs.arraydescr.tid)
            # we need to explicitely zero all the gc fields, because
            # of the unusal malloc pattern

            length = self.emit_getfield(ConstInt(frame_info),
                                        descr=descrs.jfi_frame_depth,
                                        raw=True)
            self.emit_setfield(frame,
                               self.c_zero,
                               descr=descrs.jf_extra_stack_depth)
            self.emit_setfield(frame, self.c_null, descr=descrs.jf_savedata)
            self.emit_setfield(frame, self.c_null, descr=descrs.jf_force_descr)
            self.emit_setfield(frame, self.c_null, descr=descrs.jf_descr)
            self.emit_setfield(frame, self.c_null, descr=descrs.jf_guard_exc)
            self.emit_setfield(frame, self.c_null, descr=descrs.jf_forward)
            self.gen_initialize_len(frame, length, descrs.arraydescr.lendescr)
            return self.get_box_replacement(frame)

    def emit_getfield(self, ptr, descr, type='i', raw=False):
        ofs, size, sign = unpack_fielddescr(descr)
        op = self.emit_gc_load_or_indexed(None, ptr, ConstInt(0), size, 1, ofs,
                                          sign)
        return op

    def emit_setfield(self, ptr, value, descr):
        ofs, size, sign = unpack_fielddescr(descr)
        self.emit_gc_store_or_indexed(None, ptr, ConstInt(0), value, size, 1,
                                      ofs)

    def handle_call_assembler(self, op):
        descrs = self.gc_ll_descr.getframedescrs(self.cpu)
        loop_token = op.getdescr()
        assert isinstance(loop_token, history.JitCellToken)
        jfi = loop_token.compiled_loop_token.frame_info
        llfi = heaptracker.adr2int(llmemory.cast_ptr_to_adr(jfi))
        frame = self.gen_malloc_frame(llfi)
        self.emit_setfield(frame,
                           history.ConstInt(llfi),
                           descr=descrs.jf_frame_info)
        arglist = op.getarglist()
        index_list = loop_token.compiled_loop_token._ll_initial_locs
        for i, arg in enumerate(arglist):
            descr = self.cpu.getarraydescr_for_frame(arg.type)
            assert self.cpu.JITFRAME_FIXED_SIZE & 1 == 0
            _, itemsize, _ = self.cpu.unpack_arraydescr_size(descr)
            array_offset = index_list[i]  # index, already measured in bytes
            # emit GC_STORE
            _, basesize, _ = unpack_arraydescr(descr)
            offset = basesize + array_offset
            args = [frame, ConstInt(offset), arg, ConstInt(itemsize)]
            self.emit_op(ResOperation(rop.GC_STORE, args))

        descr = op.getdescr()
        assert isinstance(descr, JitCellToken)
        jd = descr.outermost_jitdriver_sd
        args = [frame]
        if jd and jd.index_of_virtualizable >= 0:
            args = [frame, arglist[jd.index_of_virtualizable]]
        else:
            args = [frame]
        call_asm = ResOperation(op.getopnum(), args, descr=op.getdescr())
        self.replace_op_with(self.get_box_replacement(op), call_asm)
        self.emit_op(call_asm)

    # ----------

    def emitting_an_operation_that_can_collect(self):
        # must be called whenever we emit an operation that can collect:
        # forgets the previous MALLOC_NURSERY, if any; and empty the
        # set 'write_barrier_applied', so that future SETFIELDs will generate
        # a write barrier as usual.
        # it also writes down all the pending zero ptr fields
        self._op_malloc_nursery = None
        self._write_barrier_applied.clear()
        self.emit_pending_zeros()

    def write_barrier_applied(self, op):
        return self.get_box_replacement(op) in self._write_barrier_applied

    def remember_write_barrier(self, op):
        self._write_barrier_applied[self.get_box_replacement(op)] = None

    def emit_pending_zeros(self):
        # First, try to rewrite the existing ZERO_ARRAY operations from
        # the 'last_zero_arrays' list.  Note that these operation objects
        # are also already in 'newops', which is the point.
        for op in self.last_zero_arrays:
            assert op.getopnum() == rop.ZERO_ARRAY
            descr = op.getdescr()
            assert isinstance(descr, ArrayDescr)
            scale = descr.itemsize
            box = op.getarg(0)
            try:
                intset = self.setarrayitems_occurred(box)
            except KeyError:
                start_box = op.getarg(1)
                length_box = op.getarg(2)
                if isinstance(start_box, ConstInt):
                    start = start_box.getint()
                    op.setarg(1, ConstInt(start * scale))
                    op.setarg(3, ConstInt(1))
                if isinstance(length_box, ConstInt):
                    stop = length_box.getint()
                    scaled_len = stop * scale
                    op.setarg(2, ConstInt(scaled_len))
                    op.setarg(4, ConstInt(1))
                continue
            assert op.getarg(1).getint() == 0  # always 'start=0' initially
            start = 0
            while start in intset:
                start += 1
            op.setarg(1, ConstInt(start * scale))
            stop = op.getarg(2).getint()
            assert start <= stop
            while stop > start and (stop - 1) in intset:
                stop -= 1
            op.setarg(2, ConstInt((stop - start) * scale))
            # ^^ may be ConstInt(0); then the operation becomes a no-op
            op.setarg(3, ConstInt(1))  # set scale to 1
            op.setarg(4, ConstInt(1))  # set scale to 1
        del self.last_zero_arrays[:]
        self._setarrayitems_occurred.clear()
        #
        # Then write the NULL-pointer-writing ops that are still pending
        for v, d in self._delayed_zero_setfields.iteritems():
            v = self.get_box_replacement(v)
            for ofs in d.iterkeys():
                self.emit_gc_store_or_indexed(None, v, ConstInt(ofs),
                                              ConstInt(0), WORD, 1, 0)
        self._delayed_zero_setfields.clear()

    def _gen_call_malloc_gc(self, args, v_result, descr):
        """Generate a CALL_R/CHECK_MEMORY_ERROR with the given args."""
        self.emitting_an_operation_that_can_collect()
        op = ResOperation(rop.CALL_R, args, descr=descr)
        self.replace_op_with(v_result, op)
        self.emit_op(op)
        self.emit_op(ResOperation(rop.CHECK_MEMORY_ERROR, [op]))
        # In general, don't add v_result to write_barrier_applied:
        # v_result might be a large young array.

    def gen_malloc_fixedsize(self, size, typeid, v_result):
        """Generate a CALL_R(malloc_fixedsize_fn, ...).
        Used on Boehm, and on the framework GC for large fixed-size
        mallocs.  (For all I know this latter case never occurs in
        practice, but better safe than sorry.)
        """
        if self.gc_ll_descr.fielddescr_tid is not None:  # framework GC
            assert (size & (WORD - 1)) == 0, "size not aligned?"
            addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_big_fixedsize')
            args = [ConstInt(addr), ConstInt(size), ConstInt(typeid)]
            descr = self.gc_ll_descr.malloc_big_fixedsize_descr
        else:  # Boehm
            addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_fixedsize')
            args = [ConstInt(addr), ConstInt(size)]
            descr = self.gc_ll_descr.malloc_fixedsize_descr
        self._gen_call_malloc_gc(args, v_result, descr)
        # mark 'v_result' as freshly malloced, so not needing a write barrier
        # (this is always true because it's a fixed-size object)
        self.remember_write_barrier(v_result)

    def gen_boehm_malloc_array(self, arraydescr, v_num_elem, v_result):
        """Generate a CALL_R(malloc_array_fn, ...) for Boehm."""
        addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_array')
        self._gen_call_malloc_gc([
            ConstInt(addr),
            ConstInt(arraydescr.basesize), v_num_elem,
            ConstInt(arraydescr.itemsize),
            ConstInt(arraydescr.lendescr.offset)
        ], v_result, self.gc_ll_descr.malloc_array_descr)

    def gen_malloc_array(self, arraydescr, v_num_elem, v_result):
        """Generate a CALL_R(malloc_array_fn, ...) going either
        to the standard or the nonstandard version of the function."""
        #
        if (arraydescr.basesize == self.gc_ll_descr.standard_array_basesize
                and arraydescr.lendescr.offset
                == self.gc_ll_descr.standard_array_length_ofs):
            # this is a standard-looking array, common case
            addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_array')
            args = [
                ConstInt(addr),
                ConstInt(arraydescr.itemsize),
                ConstInt(arraydescr.tid), v_num_elem
            ]
            calldescr = self.gc_ll_descr.malloc_array_descr
        else:
            # rare case, so don't care too much about the number of arguments
            addr = self.gc_ll_descr.get_malloc_fn_addr(
                'malloc_array_nonstandard')
            args = [
                ConstInt(addr),
                ConstInt(arraydescr.basesize),
                ConstInt(arraydescr.itemsize),
                ConstInt(arraydescr.lendescr.offset),
                ConstInt(arraydescr.tid), v_num_elem
            ]
            calldescr = self.gc_ll_descr.malloc_array_nonstandard_descr
        self._gen_call_malloc_gc(args, v_result, calldescr)

    def gen_malloc_str(self, v_num_elem, v_result):
        """Generate a CALL_R(malloc_str_fn, ...)."""
        addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_str')
        self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result,
                                 self.gc_ll_descr.malloc_str_descr)

    def gen_malloc_unicode(self, v_num_elem, v_result):
        """Generate a CALL_R(malloc_unicode_fn, ...)."""
        addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_unicode')
        self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result,
                                 self.gc_ll_descr.malloc_unicode_descr)

    def gen_malloc_nursery_varsize(self,
                                   itemsize,
                                   v_length,
                                   v_result,
                                   arraydescr,
                                   kind=FLAG_ARRAY):
        """ itemsize is an int, v_length and v_result are boxes
        """
        gc_descr = self.gc_ll_descr
        if (kind == FLAG_ARRAY
                and (arraydescr.basesize != gc_descr.standard_array_basesize
                     or arraydescr.lendescr.offset !=
                     gc_descr.standard_array_length_ofs)):
            return False
        self.emitting_an_operation_that_can_collect()
        op = ResOperation(
            rop.CALL_MALLOC_NURSERY_VARSIZE,
            [ConstInt(kind), ConstInt(itemsize), v_length],
            descr=arraydescr)
        self.replace_op_with(v_result, op)
        self.emit_op(op)
        # don't record v_result into self.write_barrier_applied:
        # it can be a large, young array with card marking, and then
        # the GC relies on the write barrier being called
        return True

    def gen_malloc_nursery_varsize_frame(self, sizebox):
        """ Generate CALL_MALLOC_NURSERY_VARSIZE_FRAME
        """
        self.emitting_an_operation_that_can_collect()
        op = ResOperation(rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME, [sizebox])

        self.emit_op(op)
        self.remember_write_barrier(op)
        return op

    def gen_malloc_nursery(self, size, v_result):
        """Try to generate or update a CALL_MALLOC_NURSERY.
        If that succeeds, return True; you still need to write the tid.
        If that fails, return False.
        """
        size = self.round_up_for_allocation(size)
        if not self.gc_ll_descr.can_use_nursery_malloc(size):
            return False
        #
        op = None
        if self._op_malloc_nursery is not None:
            # already a MALLOC_NURSERY: increment its total size
            total_size = self._op_malloc_nursery.getarg(0).getint()
            total_size += size
            if self.gc_ll_descr.can_use_nursery_malloc(total_size):
                # if the total size is still reasonable, merge it
                self._op_malloc_nursery.setarg(0, ConstInt(total_size))
                op = ResOperation(rop.NURSERY_PTR_INCREMENT, [
                    self._v_last_malloced_nursery,
                    ConstInt(self._previous_size)
                ])
                self.replace_op_with(v_result, op)
        if op is None:
            # if we failed to merge with a previous MALLOC_NURSERY, emit one
            self.emitting_an_operation_that_can_collect()
            op = ResOperation(rop.CALL_MALLOC_NURSERY, [ConstInt(size)])
            self.replace_op_with(v_result, op)
            self._op_malloc_nursery = op
        #
        self.emit_op(op)
        self._previous_size = size
        self._v_last_malloced_nursery = op
        self.remember_write_barrier(op)
        return True

    def gen_initialize_tid(self, v_newgcobj, tid):
        if self.gc_ll_descr.fielddescr_tid is not None:
            # produce a SETFIELD to initialize the GC header
            self.emit_setfield(v_newgcobj,
                               ConstInt(tid),
                               descr=self.gc_ll_descr.fielddescr_tid)

    def gen_initialize_len(self, v_newgcobj, v_length, arraylen_descr):
        # produce a SETFIELD to initialize the array length
        self.emit_setfield(v_newgcobj, v_length, descr=arraylen_descr)

    # ----------

    def handle_write_barrier_setfield(self, op):
        val = op.getarg(0)
        if not self.write_barrier_applied(val):
            v = op.getarg(1)
            if (v.type == 'r' and (not isinstance(v, ConstPtr)
                                   or rgc.needs_write_barrier(v.value))):
                self.gen_write_barrier(val)
                #op = op.copy_and_change(rop.SETFIELD_RAW)
        self.emit_op(op)

    def handle_write_barrier_setarrayitem(self, op):
        val = op.getarg(0)
        if not self.write_barrier_applied(val):
            v = op.getarg(2)
            if (v.type == 'r' and (not isinstance(v, ConstPtr)
                                   or rgc.needs_write_barrier(v.value))):
                self.gen_write_barrier_array(val, op.getarg(1))
                #op = op.copy_and_change(rop.SET{ARRAYITEM,INTERIORFIELD}_RAW)
        self.emit_op(op)

    handle_write_barrier_setinteriorfield = handle_write_barrier_setarrayitem

    def gen_write_barrier(self, v_base):
        write_barrier_descr = self.gc_ll_descr.write_barrier_descr
        args = [v_base]
        self.emit_op(
            ResOperation(rop.COND_CALL_GC_WB, args, descr=write_barrier_descr))
        self.remember_write_barrier(v_base)

    def gen_write_barrier_array(self, v_base, v_index):
        write_barrier_descr = self.gc_ll_descr.write_barrier_descr
        if write_barrier_descr.has_write_barrier_from_array(self.cpu):
            # If we know statically the length of 'v', and it is not too
            # big, then produce a regular write_barrier.  If it's unknown or
            # too big, produce instead a write_barrier_from_array.
            LARGE = 130
            length = self.known_length(v_base, LARGE)
            if length >= LARGE:
                # unknown or too big: produce a write_barrier_from_array
                args = [v_base, v_index]
                self.emit_op(
                    ResOperation(rop.COND_CALL_GC_WB_ARRAY,
                                 args,
                                 descr=write_barrier_descr))
                # a WB_ARRAY is not enough to prevent any future write
                # barriers, so don't add to 'write_barrier_applied'!
                return
        # fall-back case: produce a write_barrier
        self.gen_write_barrier(v_base)

    def round_up_for_allocation(self, size):
        if not self.gc_ll_descr.round_up:
            return size
        if self.gc_ll_descr.translate_support_code:
            from rpython.rtyper.lltypesystem import llarena
            return llarena.round_up_for_allocation(
                size, self.gc_ll_descr.minimal_size_in_nursery)
        else:
            # non-translated: do it manually
            # assume that "self.gc_ll_descr.minimal_size_in_nursery" is 2 WORDs
            size = max(size, 2 * WORD)
            return (size + WORD - 1) & ~(WORD - 1)  # round up

    def remove_bridge_exception(self, operations):
        """Check a common case: 'save_exception' immediately followed by
        'restore_exception' at the start of the bridge."""
        # XXX should check if the boxes are used later; but we just assume
        # they aren't for now
        start = 0
        if operations[0].getopnum() == rop.INCREMENT_DEBUG_COUNTER:
            start = 1
        if len(operations) >= start + 3:
            if (operations[start + 0].getopnum() == rop.SAVE_EXC_CLASS
                    and operations[start + 1].getopnum() == rop.SAVE_EXCEPTION
                    and operations[start + 2].getopnum()
                    == rop.RESTORE_EXCEPTION):
                return operations[:start] + operations[start + 3:]
        return operations

    def emit_label(self):
        self.emitting_an_operation_that_can_collect()
        self._known_lengths.clear()
        self.gcrefs_recently_loaded = None

    def _gcref_index(self, gcref):
        if self.gcrefs_map is None:
            self.gcrefs_map = r_dict(rd_eq, rd_hash)
        try:
            return self.gcrefs_map[gcref]
        except KeyError:
            pass
        index = len(self.gcrefs_output_list)
        self.gcrefs_map[gcref] = index
        self.gcrefs_output_list.append(gcref)
        return index

    def remove_constptr(self, c):
        """Remove all ConstPtrs, and replace them with load_from_gc_table.
        """
        # Note: currently, gcrefs_recently_loaded is only cleared in
        # LABELs.  We'd like something better, like "don't spill it",
        # but that's the wrong level...
        index = self._gcref_index(c.value)
        if self.gcrefs_recently_loaded is None:
            self.gcrefs_recently_loaded = {}
        try:
            load_op = self.gcrefs_recently_loaded[index]
        except KeyError:
            load_op = ResOperation(rop.LOAD_FROM_GC_TABLE, [ConstInt(index)])
            self._newops.append(load_op)
            self.gcrefs_recently_loaded[index] = load_op
        return load_op
Beispiel #5
0
from rpython.jit.metainterp.resoperation import rop
from rpython.jit.metainterp.history import ConstInt, BoxInt, BasicFailDescr

box1 = "box1"
box2 = "box2"
box3 = "box3"
box4 = "box4"
box5 = "box5"
lengthbox1 = object()
lengthbox2 = object()
lengthbox3 = object()
descr1 = object()
descr2 = object()
descr3 = object()

index1 = ConstInt(0)
index2 = ConstInt(1)


class FakeEffectinfo(object):
    EF_ELIDABLE_CANNOT_RAISE = 0  #elidable function (and cannot raise)
    EF_LOOPINVARIANT = 1  #special: call it only once per loop
    EF_CANNOT_RAISE = 2  #a function which cannot raise
    EF_ELIDABLE_OR_MEMORYERROR = 3
    EF_ELIDABLE_CAN_RAISE = 4  #elidable function (but can raise)
    EF_CAN_RAISE = 5  #normal function (can raise)
    EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE = 6  #can raise and force virtualizables
    EF_RANDOM_EFFECTS = 7  #can do whatever

    OS_ARRAYCOPY = 0
Beispiel #6
0
 def _append_debugging_code(self, operations, tp, number, token):
     counter = self._register_counter(tp, number, token)
     c_adr = ConstInt(rffi.cast(lltype.Signed, counter))
     operations.append(
         ResOperation(rop.INCREMENT_DEBUG_COUNTER, [c_adr]))
Beispiel #7
0
 def getstrlen(self, op, string_optimizer, mode):
     length = self.getstrlen1(mode)
     if length < 0:
         return None
     return ConstInt(length)
Beispiel #8
0
 def gen_malloc_unicode(self, v_num_elem, v_result):
     """Generate a CALL_MALLOC_GC(malloc_unicode_fn, ...)."""
     addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_unicode')
     self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result,
                              self.gc_ll_descr.malloc_unicode_descr)
def serialize_optimizer_knowledge(optimizer, numb_state, liveboxes,
                                  liveboxes_from_env, memo):
    from rpython.jit.metainterp.history import ConstInt
    available_boxes = {}
    for box in liveboxes:
        if box is not None and box in liveboxes_from_env:
            available_boxes[box] = None

    # class knowledge is stored as bits, true meaning the class is known, false
    # means unknown. on deserializing we look at the bits, and read the runtime
    # class for the known classes (which has to be the same in the bridge) and
    # mark that as known. this works for guard_class too: the class is only
    # known *after* the guard
    bitfield = 0
    shifts = 0
    for box in liveboxes:
        if box is None or box.type != "r":
            continue
        info = optimizer.getptrinfo(box)
        known_class = info is not None and info.get_known_class(
            optimizer.cpu) is not None
        bitfield <<= 1
        bitfield |= known_class
        shifts += 1
        if shifts == 6:
            numb_state.append_int(bitfield)
            bitfield = shifts = 0
    if shifts:
        numb_state.append_int(bitfield << (6 - shifts))

    # heap knowledge: we store triples of known heap fields in non-virtual
    # structs
    if optimizer.optheap:
        triples_struct, triples_array = optimizer.optheap.serialize_optheap(
            available_boxes)
        # can only encode descrs that have a known index into
        # metainterp_sd.all_descrs
        triples_struct = [
            triple for triple in triples_struct if triple[1].descr_index != -1
        ]
        numb_state.append_int(len(triples_struct))
        for box1, descr, box2 in triples_struct:
            descr_index = descr.descr_index
            numb_state.append_short(tag_box(box1, liveboxes_from_env, memo))
            numb_state.append_int(descr_index)
            numb_state.append_short(tag_box(box2, liveboxes_from_env, memo))
        numb_state.append_int(len(triples_array))
        for box1, index, descr, box2 in triples_array:
            descr_index = descr.descr_index
            numb_state.append_short(tag_box(box1, liveboxes_from_env, memo))
            numb_state.append_int(index)
            numb_state.append_int(descr_index)
            numb_state.append_short(tag_box(box2, liveboxes_from_env, memo))
    else:
        numb_state.append_int(0)
        numb_state.append_int(0)

    if optimizer.optrewrite:
        tuples_loopinvariant = optimizer.optrewrite.serialize_optrewrite(
            available_boxes)
        numb_state.append_int(len(tuples_loopinvariant))
        for constarg0, box in tuples_loopinvariant:
            numb_state.append_short(
                tag_box(ConstInt(constarg0), liveboxes_from_env, memo))
            numb_state.append_short(tag_box(box, liveboxes_from_env, memo))
    else:
        numb_state.append_int(0)
Beispiel #10
0
 def prepare_op_guard_no_exception(self, op):
     loc = self.make_sure_var_in_reg(ConstInt(self.cpu.pos_exception()))
     return [loc] + self._guard_impl(op)
Beispiel #11
0
 def produce_into(self, builder, r):
     v, offset = self.field_descr(builder, r)
     builder.do(self.opnum, [v, ConstInt(offset)], None)
Beispiel #12
0
def test_virtual_adder_make_virtual():
    b2s, b3s, b4s, b5s = [RefFrontendOp(0), IntFrontendOp(0), RefFrontendOp(0),
                          RefFrontendOp(0)]
    c1s = ConstInt(111)
    storage = Storage()
    memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
    modifier = ResumeDataVirtualAdder(FakeOptimizer(), storage, storage, None, memo)
    modifier.liveboxes_from_env = {}
    modifier.liveboxes = {}
    modifier.vfieldboxes = {}

    vdescr = LLtypeMixin.nodesize2
    ca = ConstInt(ptr2int(LLtypeMixin.node_vtable2))
    v4 = info.InstancePtrInfo(vdescr, ca, True)
    b4s.set_forwarded(v4)
    v4.setfield(LLtypeMixin.nextdescr, ca, b2s)
    v4.setfield(LLtypeMixin.valuedescr, ca, b3s)
    v4.setfield(LLtypeMixin.otherdescr, ca, b5s)
    ca = ConstInt(ptr2int(LLtypeMixin.node_vtable))
    v2 = info.InstancePtrInfo(LLtypeMixin.nodesize, ca, True)
    v2.setfield(LLtypeMixin.nextdescr, b4s, ca)
    v2.setfield(LLtypeMixin.valuedescr, c1s, ca)
    b2s.set_forwarded(v2)

    modifier.register_virtual_fields(b2s, [c1s, None, None, None, b4s])
    modifier.register_virtual_fields(b4s, [b3s, None, None, None, b2s, b5s])

    liveboxes = []
    modifier._number_virtuals(liveboxes, 0)
    storage.rd_consts = memo.consts[:]
    storage.rd_numb = Numbering([0])
    # resume
    b3t, b5t = [IntFrontendOp(0), RefFrontendOp(0)]
    b5t.setref_base(demo55o)
    b3t.setint(33)
    newboxes = _resume_remap(liveboxes, [#b2s -- virtual
                                         b3s,
                                         #b4s -- virtual
                                         #b2s -- again, shared
                                         #b3s -- again, shared
                                         b5s], b3t, b5t)

    metainterp = MyMetaInterp()
    reader = ResumeDataFakeReader(storage, newboxes, metainterp)
    assert len(reader.virtuals_cache.virtuals_ptr_cache) == 2
    b2t = reader.decode_ref(modifier._gettagged(b2s))
    b4t = reader.decode_ref(modifier._gettagged(b4s))
    trace = metainterp.trace
    b2new = (rop.NEW_WITH_VTABLE, [], b2t.getref_base(), LLtypeMixin.nodesize)
    b4new = (rop.NEW_WITH_VTABLE, [], b4t.getref_base(), LLtypeMixin.nodesize2)
    b2set = [(rop.SETFIELD_GC, [b2t, b4t],      None, LLtypeMixin.nextdescr),
             (rop.SETFIELD_GC, [b2t, c1s],      None, LLtypeMixin.valuedescr)]
    b4set = [(rop.SETFIELD_GC, [b4t, b2t],     None, LLtypeMixin.nextdescr),
             (rop.SETFIELD_GC, [b4t, b3t],     None, LLtypeMixin.valuedescr),
             (rop.SETFIELD_GC, [b4t, b5t],     None, LLtypeMixin.otherdescr)]
    expected = [b2new, b4new] + b4set + b2set

    # check that we get the operations in 'expected', in a possibly different
    # order.
    assert len(trace) == len(expected)
    orig = trace[:]
    with CompareableConsts():
        for x in trace:
            assert x in expected
            expected.remove(x)

    ptr = b2t.getref_base()._obj.container._as_ptr()
    assert lltype.typeOf(ptr) == lltype.Ptr(LLtypeMixin.NODE)
    assert ptr.value == 111
    ptr2 = ptr.next
    ptr2 = lltype.cast_pointer(lltype.Ptr(LLtypeMixin.NODE2), ptr2)
    assert ptr2.other == demo55
    assert ptr2.parent.value == 33
    assert ptr2.parent.next == ptr
Beispiel #13
0
def test_ResumeDataLoopMemo_number():
    b1, b2, b3, b4, b5 = [IntFrontendOp(0), IntFrontendOp(1), IntFrontendOp(2),
                          RefFrontendOp(3), RefFrontendOp(4)]
    c1, c2, c3, c4 = [ConstInt(1), ConstInt(2), ConstInt(3), ConstInt(4)]

    env = [b1, c1, b2, b1, c2]
    metainterp_sd = FakeMetaInterpStaticData()
    t = Trace([b1, b2, b3, b4, b5], metainterp_sd)
    snap = t.create_snapshot(FakeJitCode("jitcode", 0), 0, Frame(env), False)
    env1 = [c3, b3, b1, c1]
    t.append(0) # descr index
    snap1 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 2, Frame(env1), False,
        [], [])
    snap1.prev = snap

    env2 = [c3, b3, b1, c3]
    env3 = [c3, b3, b1, c3]
    env4 = [c3, b4, b1, c3]
    env5 = [b1, b4, b5]

    memo = ResumeDataLoopMemo(metainterp_sd)

    iter = t.get_iter()
    b1, b2, b3, b4, b5 = iter.inputargs
    numb_state = memo.number(0, iter)
    numb = numb_state.create_numbering()
    assert numb_state.num_virtuals == 0

    assert numb_state.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
                                    b3: tag(2, TAGBOX)}
    base = [0, 0, tag(0, TAGBOX), tag(1, TAGINT),
            tag(1, TAGBOX), tag(0, TAGBOX), tag(2, TAGINT)]

    assert unpack_numbering(numb) == [17, 0, 0, 0] + base + [0, 2, tag(3, TAGINT), tag(2, TAGBOX),
                                      tag(0, TAGBOX), tag(1, TAGINT)]
    t.append(0)
    snap2 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 2, Frame(env2),
                                  False, [], [])
    snap2.prev = snap

    numb_state2 = memo.number(1, iter)
    numb2 = numb_state2.create_numbering()
    assert numb_state2.num_virtuals == 0

    assert numb_state2.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
                                     b3: tag(2, TAGBOX)}
    assert numb_state2.liveboxes is not numb_state.liveboxes
    assert unpack_numbering(numb2) == [17, 0, 0, 0] + base + [0, 2, tag(3, TAGINT), tag(2, TAGBOX),
                                       tag(0, TAGBOX), tag(3, TAGINT)]

    t.append(0)
    snap3 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 2, Frame([]),
                                  False, [], env3)
    snap3.prev = snap

    class FakeVirtualInfo(info.AbstractVirtualPtrInfo):
        def __init__(self, virt):
            self._is_virtual = virt

        def is_virtual(self):
            return self._is_virtual

    # renamed
    b3.set_forwarded(c4)
    numb_state3 = memo.number(2, iter)
    numb3 = numb_state3.create_numbering()
    assert numb_state3.num_virtuals == 0

    assert numb_state3.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX)}
    assert unpack_numbering(numb3) == ([17, 0, 0, 2, tag(3, TAGINT), tag(4, TAGINT),
                                       tag(0, TAGBOX), tag(3, TAGINT)] +
                                       base + [0, 2])

    # virtual
    t.append(0)
    snap4 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 2, Frame([]),
                                  False, [], env4)
    snap4.prev = snap

    b4.set_forwarded(FakeVirtualInfo(True))
    numb_state4 = memo.number(3, iter)
    numb4 = numb_state4.create_numbering()
    assert numb_state4.num_virtuals == 1

    assert numb_state4.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
                                     b4: tag(0, TAGVIRTUAL)}
    assert unpack_numbering(numb4) == [17, 0, 0, 2, tag(3, TAGINT), tag(0, TAGVIRTUAL),
                                       tag(0, TAGBOX), tag(3, TAGINT)] + base + [0, 2]

    t.append(0)
    snap4 = t.create_snapshot(FakeJitCode("jitcode", 2), 1, Frame(env4), False)
    t.append(0)
    snap4.prev = snap
    snap5 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 0, Frame([]), False,
                                  env5, [])
    snap5.prev = snap4

    b4.set_forwarded(FakeVirtualInfo(True))
    b5.set_forwarded(FakeVirtualInfo(True))
    numb_state5 = memo.number(4, iter)
    numb5 = numb_state5.create_numbering()
    assert numb_state5.num_virtuals == 2

    assert numb_state5.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
                                     b4: tag(0, TAGVIRTUAL), b5: tag(1, TAGVIRTUAL)}
    assert unpack_numbering(numb5) == [22, 0,
        3, tag(0, TAGBOX), tag(0, TAGVIRTUAL), tag(1, TAGVIRTUAL),
        0] + base + [
        2, 1, tag(3, TAGINT), tag(0, TAGVIRTUAL), tag(0, TAGBOX), tag(3, TAGINT)
        ] + [0, 0]
Beispiel #14
0
def test_virtual_adder_pending_fields_and_arrayitems():
    class Storage(object):
        pass
    storage = Storage()
    modifier = ResumeDataVirtualAdder(None, storage, storage, None, None)
    modifier._add_pending_fields([])
    assert not storage.rd_pendingfields
    #
    class FieldDescr(AbstractDescr):
        def is_array_of_primitives(self):
            return False
    field_a = FieldDescr()
    storage = Storage()
    modifier = ResumeDataVirtualAdder(None, storage, storage, None, None)
    a = IntFrontendOp(0)
    b = IntFrontendOp(0)
    modifier.liveboxes_from_env = {a: rffi.cast(rffi.SHORT, 1042),
                                   b: rffi.cast(rffi.SHORT, 1061)}
    modifier._add_pending_fields(
        [ResOperation(rop.SETFIELD_GC, [a, b], descr=field_a)])
    pf = storage.rd_pendingfields
    assert len(pf) == 1
    assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[0].lldescr)
            is field_a)
    assert rffi.cast(lltype.Signed, pf[0].num) == 1042
    assert rffi.cast(lltype.Signed, pf[0].fieldnum) == 1061
    assert rffi.cast(lltype.Signed, pf[0].itemindex) == -1
    #
    array_a = FieldDescr()
    storage = Storage()
    modifier = ResumeDataVirtualAdder(None, storage, storage, None, None)
    a42 = IntFrontendOp(0)
    a61 = IntFrontendOp(0)
    a62 = IntFrontendOp(0)
    a63 = IntFrontendOp(0)
    modifier.liveboxes_from_env = {a42: rffi.cast(rffi.SHORT, 1042),
                                   a61: rffi.cast(rffi.SHORT, 1061),
                                   a62: rffi.cast(rffi.SHORT, 1062),
                                   a63: rffi.cast(rffi.SHORT, 1063)}
    modifier._add_pending_fields([
        ResOperation(rop.SETARRAYITEM_GC, [a42, ConstInt(0), a61],
                     descr=array_a),
        ResOperation(rop.SETARRAYITEM_GC, [a42, ConstInt(2147483647), a62],
                     descr=array_a)])
    pf = storage.rd_pendingfields
    assert len(pf) == 2
    assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[0].lldescr)
            is array_a)
    assert rffi.cast(lltype.Signed, pf[0].num) == 1042
    assert rffi.cast(lltype.Signed, pf[0].fieldnum) == 1061
    assert rffi.cast(lltype.Signed, pf[0].itemindex) == 0
    assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[1].lldescr)
            is array_a)
    assert rffi.cast(lltype.Signed, pf[1].num) == 1042
    assert rffi.cast(lltype.Signed, pf[1].fieldnum) == 1062
    assert rffi.cast(lltype.Signed, pf[1].itemindex) == 2147483647
    #
    if sys.maxint >= 2147483648:
        with py.test.raises(TagOverflow):
            modifier._add_pending_fields(
                [ResOperation(rop.SETARRAYITEM_GC,
                    [a42, ConstInt(2147483648), a63], descr=array_a)])
Beispiel #15
0
 def test_loc_of_const(self):
     rm = RegisterManager({})
     rm.next_instruction()
     assert isinstance(rm.loc(ConstInt(1)), ConstInt)
Beispiel #16
0
 def get_funcbox(cls, cpu, func_ptr):
     return ConstInt(ptr2int(func_ptr))
Beispiel #17
0
    def setup_class(cls):
        if cls.runappdirect:
            py.test.skip("Can't run this test with -A")
        w_f = cls.space.appexec([], """():
        def function():
            pass
        return function
        """)
        cls.w_f = w_f
        ll_code = cast_instance_to_base_ptr(w_f.code)
        code_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, ll_code)
        logger = Logger(MockSD())

        oplist = parse("""
        [i1, i2, p2]
        i3 = int_add(i1, i2)
        debug_merge_point(0, 0, 0, 0, 0, ConstPtr(ptr0))
        guard_nonnull(p2) []
        guard_true(i3) []
        """,
                       namespace={
                           'ptr0': code_gcref
                       }).operations
        greenkey = [ConstInt(0), ConstInt(0), ConstPtr(code_gcref)]
        offset = {}
        for i, op in enumerate(oplist):
            if i != 1:
                offset[op] = i

        oplist_no_descrs = parse("""
        [i1, i2, p2]
        i3 = int_add(i1, i2)
        debug_merge_point(0, 0, 0, 0, 0, ConstPtr(ptr0))
        guard_nonnull(p2) []
        guard_true(i3) []
        """,
                                 namespace={
                                     'ptr0': code_gcref
                                 }).operations
        for op in oplist_no_descrs:
            if op.is_guard():
                op.setdescr(None)

        class FailDescr(BasicFailDescr):
            def get_jitcounter_hash(self):
                from rpython.rlib.rarithmetic import r_uint
                return r_uint(13)

        oplist[-1].setdescr(FailDescr())
        oplist[-2].setdescr(FailDescr())

        token = JitCellToken()
        token.number = 0
        di_loop = JitDebugInfo(MockJitDriverSD, logger, token, oplist, 'loop',
                               greenkey)
        di_loop_optimize = JitDebugInfo(MockJitDriverSD, logger,
                                        JitCellToken(), oplist, 'loop',
                                        greenkey)
        di_loop.asminfo = AsmInfo(offset, 0x42, 12)
        di_bridge = JitDebugInfo(MockJitDriverSD,
                                 logger,
                                 JitCellToken(),
                                 oplist,
                                 'bridge',
                                 fail_descr=FailDescr())
        di_bridge.asminfo = AsmInfo(offset, 0, 0)

        def interp_on_compile():
            di_loop.oplist = cls.oplist
            if pypy_hooks.are_hooks_enabled():
                pypy_hooks.after_compile(di_loop)

        def interp_on_compile_bridge():
            if pypy_hooks.are_hooks_enabled():
                pypy_hooks.after_compile_bridge(di_bridge)

        def interp_on_optimize():
            if pypy_hooks.are_hooks_enabled():
                di_loop_optimize.oplist = cls.oplist
                pypy_hooks.before_compile(di_loop_optimize)

        def interp_on_abort():
            if pypy_hooks.are_hooks_enabled():
                pypy_hooks.on_abort(Counters.ABORT_TOO_LONG,
                                    pypyjitdriver, greenkey, 'blah',
                                    Logger(MockSD), cls.oplist_no_descrs)

        space = cls.space
        cls.w_on_compile = space.wrap(interp2app(interp_on_compile))
        cls.w_on_compile_bridge = space.wrap(
            interp2app(interp_on_compile_bridge))
        cls.w_on_abort = space.wrap(interp2app(interp_on_abort))
        cls.w_int_add_num = space.wrap(rop.INT_ADD)
        cls.w_dmp_num = space.wrap(rop.DEBUG_MERGE_POINT)
        cls.w_on_optimize = space.wrap(interp2app(interp_on_optimize))
        cls.orig_oplist = oplist
        cls.orig_oplist_no_descrs = oplist_no_descrs
        cls.w_sorted_keys = space.wrap(sorted(Counters.counter_names))
Beispiel #18
0
class GcRewriterAssembler(object):
    """ This class performs the following rewrites on the list of operations:

     - Turn all NEW_xxx to either a CALL_MALLOC_GC, or a CALL_MALLOC_NURSERY
       followed by SETFIELDs in order to initialize their GC fields.  The
       two advantages of CALL_MALLOC_NURSERY is that it inlines the common
       path, and we need only one such operation to allocate several blocks
       of memory at once.

     - Add COND_CALLs to the write barrier before SETFIELD_GC and
       SETARRAYITEM_GC operations.

    'write_barrier_applied' contains a dictionary of variable -> None.
    If a variable is in the dictionary, next setfields can be called without
    a write barrier.  The idea is that an object that was freshly allocated
    or already write_barrier'd don't need another write_barrier if there
    was no potentially collecting resop inbetween.
    """

    _previous_size = -1
    _op_malloc_nursery = None
    _v_last_malloced_nursery = None
    c_zero = ConstInt(0)
    c_null = ConstPtr(lltype.nullptr(llmemory.GCREF.TO))

    def __init__(self, gc_ll_descr, cpu):
        self.gc_ll_descr = gc_ll_descr
        self.cpu = cpu
        self.newops = []
        self.known_lengths = {}
        self.write_barrier_applied = {}
        self.delayed_zero_setfields = {}
        self.last_zero_arrays = []
        self.setarrayitems_occurred = {}  # {box: {set-of-indexes}}

    def rewrite(self, operations):
        # we can only remember one malloc since the next malloc can possibly
        # collect; but we can try to collapse several known-size mallocs into
        # one, both for performance and to reduce the number of write
        # barriers.  We do this on each "basic block" of operations, which in
        # this case means between CALLs or unknown-size mallocs.
        #
        for i in range(len(operations)):
            op = operations[i]
            if op.getopnum() == rop.DEBUG_MERGE_POINT:
                continue
            # ---------- GETFIELD_GC ----------
            if op.getopnum() == rop.GETFIELD_GC:
                self.handle_getfield_gc(op)
                continue
            # ---------- turn NEWxxx into CALL_MALLOC_xxx ----------
            if op.is_malloc():
                self.handle_malloc_operation(op)
                continue
            if (op.is_guard()
                    or self.could_merge_with_next_guard(op, i, operations)):
                self.emit_pending_zeros()
            elif op.can_malloc():
                self.emitting_an_operation_that_can_collect()
            elif op.getopnum() == rop.DEBUG_MERGE_POINT:
                continue  # ignore debug_merge_points
            elif op.getopnum() == rop.LABEL:
                self.emitting_an_operation_that_can_collect()
                self.known_lengths.clear()
            # ---------- write barriers ----------
            if self.gc_ll_descr.write_barrier_descr is not None:
                if op.getopnum() == rop.SETFIELD_GC:
                    self.consider_setfield_gc(op)
                    self.handle_write_barrier_setfield(op)
                    continue
                if op.getopnum() == rop.SETINTERIORFIELD_GC:
                    self.handle_write_barrier_setinteriorfield(op)
                    continue
                if op.getopnum() == rop.SETARRAYITEM_GC:
                    self.consider_setarrayitem_gc(op)
                    self.handle_write_barrier_setarrayitem(op)
                    continue
            else:
                # this is dead code, but in case we have a gc that does
                # not have a write barrier and does not zero memory, we would
                # need to clal it
                if op.getopnum() == rop.SETFIELD_GC:
                    self.consider_setfield_gc(op)
                elif op.getopnum() == rop.SETARRAYITEM_GC:
                    self.consider_setarrayitem_gc(op)
            # ---------- call assembler -----------
            if op.getopnum() == rop.CALL_ASSEMBLER:
                self.handle_call_assembler(op)
                continue
            if op.getopnum() == rop.JUMP or op.getopnum() == rop.FINISH:
                self.emit_pending_zeros()
            #
            self.newops.append(op)
        return self.newops

    def could_merge_with_next_guard(self, op, i, operations):
        # return True in cases where the operation and the following guard
        # should likely remain together.  Simplified version of
        # can_merge_with_next_guard() in llsupport/regalloc.py.
        if not op.is_comparison():
            return op.is_ovf()  # int_xxx_ovf() / guard_no_overflow()
        if i + 1 >= len(operations):
            return False
        if (operations[i + 1].getopnum() != rop.GUARD_TRUE
                and operations[i + 1].getopnum() != rop.GUARD_FALSE):
            return False
        if operations[i + 1].getarg(0) is not op.result:
            return False
        return True

    # ----------

    def handle_getfield_gc(self, op):
        """See test_zero_ptr_field_before_getfield().  We hope there is
        no getfield_gc in the middle of initialization code, but there
        shouldn't be, given that a 'new' is already delayed by previous
        optimization steps.  In practice it should immediately be
        followed by a bunch of 'setfields', and the 'pending_zeros'
        optimization we do here is meant for this case."""
        self.emit_pending_zeros()
        self.newops.append(op)

    # ----------

    def handle_malloc_operation(self, op):
        opnum = op.getopnum()
        if opnum == rop.NEW:
            self.handle_new_fixedsize(op.getdescr(), op)
        elif opnum == rop.NEW_WITH_VTABLE:
            classint = op.getarg(0).getint()
            descr = heaptracker.vtable2descr(self.cpu, classint)
            self.handle_new_fixedsize(descr, op)
            if self.gc_ll_descr.fielddescr_vtable is not None:
                op = ResOperation(rop.SETFIELD_GC,
                                  [op.result, ConstInt(classint)],
                                  None,
                                  descr=self.gc_ll_descr.fielddescr_vtable)
                self.newops.append(op)
        elif opnum == rop.NEW_ARRAY or opnum == rop.NEW_ARRAY_CLEAR:
            descr = op.getdescr()
            assert isinstance(descr, ArrayDescr)
            self.handle_new_array(descr, op)
        elif opnum == rop.NEWSTR:
            self.handle_new_array(self.gc_ll_descr.str_descr,
                                  op,
                                  kind=FLAG_STR)
        elif opnum == rop.NEWUNICODE:
            self.handle_new_array(self.gc_ll_descr.unicode_descr,
                                  op,
                                  kind=FLAG_UNICODE)
        else:
            raise NotImplementedError(op.getopname())

    def clear_gc_fields(self, descr, result):
        if self.gc_ll_descr.malloc_zero_filled:
            return
        try:
            d = self.delayed_zero_setfields[result]
        except KeyError:
            d = {}
            self.delayed_zero_setfields[result] = d
        for fielddescr in descr.gc_fielddescrs:
            ofs = self.cpu.unpack_fielddescr(fielddescr)
            d[ofs] = None

    def consider_setfield_gc(self, op):
        offset = self.cpu.unpack_fielddescr(op.getdescr())
        try:
            del self.delayed_zero_setfields[op.getarg(0)][offset]
        except KeyError:
            pass

    def consider_setarrayitem_gc(self, op):
        array_box = op.getarg(0)
        index_box = op.getarg(1)
        if isinstance(array_box, BoxPtr) and isinstance(index_box, ConstInt):
            try:
                intset = self.setarrayitems_occurred[array_box]
            except KeyError:
                intset = self.setarrayitems_occurred[array_box] = {}
            intset[index_box.getint()] = None

    def clear_varsize_gc_fields(self, kind, descr, result, v_length, opnum):
        if self.gc_ll_descr.malloc_zero_filled:
            return
        if kind == FLAG_ARRAY:
            if descr.is_array_of_structs() or descr.is_array_of_pointers():
                assert opnum == rop.NEW_ARRAY_CLEAR
            if opnum == rop.NEW_ARRAY_CLEAR:
                self.handle_clear_array_contents(descr, result, v_length)
            return
        if kind == FLAG_STR:
            hash_descr = self.gc_ll_descr.str_hash_descr
        elif kind == FLAG_UNICODE:
            hash_descr = self.gc_ll_descr.unicode_hash_descr
        else:
            return
        op = ResOperation(rop.SETFIELD_GC, [result, self.c_zero],
                          None,
                          descr=hash_descr)
        self.newops.append(op)

    def handle_new_fixedsize(self, descr, op):
        assert isinstance(descr, SizeDescr)
        size = descr.size
        if self.gen_malloc_nursery(size, op.result):
            self.gen_initialize_tid(op.result, descr.tid)
        else:
            self.gen_malloc_fixedsize(size, descr.tid, op.result)
        self.clear_gc_fields(descr, op.result)

    def handle_new_array(self, arraydescr, op, kind=FLAG_ARRAY):
        v_length = op.getarg(0)
        total_size = -1
        if isinstance(v_length, ConstInt):
            num_elem = v_length.getint()
            self.known_lengths[op.result] = num_elem
            try:
                var_size = ovfcheck(arraydescr.itemsize * num_elem)
                total_size = ovfcheck(arraydescr.basesize + var_size)
            except OverflowError:
                pass  # total_size is still -1
        elif arraydescr.itemsize == 0:
            total_size = arraydescr.basesize
        elif (self.gc_ll_descr.can_use_nursery_malloc(1)
              and self.gen_malloc_nursery_varsize(arraydescr.itemsize,
                                                  v_length,
                                                  op.result,
                                                  arraydescr,
                                                  kind=kind)):
            # note that we cannot initialize tid here, because the array
            # might end up being allocated by malloc_external or some
            # stuff that initializes GC header fields differently
            self.gen_initialize_len(op.result, v_length, arraydescr.lendescr)
            self.clear_varsize_gc_fields(kind, op.getdescr(), op.result,
                                         v_length, op.getopnum())
            return
        if (total_size >= 0
                and self.gen_malloc_nursery(total_size, op.result)):
            self.gen_initialize_tid(op.result, arraydescr.tid)
            self.gen_initialize_len(op.result, v_length, arraydescr.lendescr)
        elif self.gc_ll_descr.kind == 'boehm':
            self.gen_boehm_malloc_array(arraydescr, v_length, op.result)
        else:
            opnum = op.getopnum()
            if opnum == rop.NEW_ARRAY or opnum == rop.NEW_ARRAY_CLEAR:
                self.gen_malloc_array(arraydescr, v_length, op.result)
            elif opnum == rop.NEWSTR:
                self.gen_malloc_str(v_length, op.result)
            elif opnum == rop.NEWUNICODE:
                self.gen_malloc_unicode(v_length, op.result)
            else:
                raise NotImplementedError(op.getopname())
        self.clear_varsize_gc_fields(kind, op.getdescr(), op.result, v_length,
                                     op.getopnum())

    def handle_clear_array_contents(self, arraydescr, v_arr, v_length):
        assert v_length is not None
        if isinstance(v_length, ConstInt) and v_length.getint() == 0:
            return
        # the ZERO_ARRAY operation will be optimized according to what
        # SETARRAYITEM_GC we see before the next allocation operation.
        # See emit_pending_zeros().
        o = ResOperation(rop.ZERO_ARRAY, [v_arr, self.c_zero, v_length],
                         None,
                         descr=arraydescr)
        self.newops.append(o)
        if isinstance(v_length, ConstInt):
            self.last_zero_arrays.append(o)

    def gen_malloc_frame(self, frame_info, frame, size_box):
        descrs = self.gc_ll_descr.getframedescrs(self.cpu)
        if self.gc_ll_descr.kind == 'boehm':
            op0 = ResOperation(rop.GETFIELD_RAW,
                               [history.ConstInt(frame_info)],
                               size_box,
                               descr=descrs.jfi_frame_depth)
            self.newops.append(op0)
            op1 = ResOperation(rop.NEW_ARRAY, [size_box],
                               frame,
                               descr=descrs.arraydescr)
            self.handle_new_array(descrs.arraydescr, op1)
        else:
            # we read size in bytes here, not the length
            op0 = ResOperation(rop.GETFIELD_RAW,
                               [history.ConstInt(frame_info)],
                               size_box,
                               descr=descrs.jfi_frame_size)
            self.newops.append(op0)
            self.gen_malloc_nursery_varsize_frame(size_box, frame)
            self.gen_initialize_tid(frame, descrs.arraydescr.tid)
            length_box = history.BoxInt()
            # we need to explicitely zero all the gc fields, because
            # of the unusal malloc pattern
            extra_ops = [
                ResOperation(rop.GETFIELD_RAW, [history.ConstInt(frame_info)],
                             length_box,
                             descr=descrs.jfi_frame_depth),
                ResOperation(rop.SETFIELD_GC, [frame, self.c_zero],
                             None,
                             descr=descrs.jf_extra_stack_depth),
                ResOperation(rop.SETFIELD_GC, [frame, self.c_null],
                             None,
                             descr=descrs.jf_savedata),
                ResOperation(rop.SETFIELD_GC, [frame, self.c_null],
                             None,
                             descr=descrs.jf_force_descr),
                ResOperation(rop.SETFIELD_GC, [frame, self.c_null],
                             None,
                             descr=descrs.jf_descr),
                ResOperation(rop.SETFIELD_GC, [frame, self.c_null],
                             None,
                             descr=descrs.jf_guard_exc),
                ResOperation(rop.SETFIELD_GC, [frame, self.c_null],
                             None,
                             descr=descrs.jf_forward),
            ]
            self.newops += extra_ops
            self.gen_initialize_len(frame, length_box,
                                    descrs.arraydescr.lendescr)

    def handle_call_assembler(self, op):
        descrs = self.gc_ll_descr.getframedescrs(self.cpu)
        loop_token = op.getdescr()
        assert isinstance(loop_token, history.JitCellToken)
        jfi = loop_token.compiled_loop_token.frame_info
        llfi = heaptracker.adr2int(llmemory.cast_ptr_to_adr(jfi))
        size_box = history.BoxInt()
        frame = history.BoxPtr()
        self.gen_malloc_frame(llfi, frame, size_box)
        op2 = ResOperation(rop.SETFIELD_GC,
                           [frame, history.ConstInt(llfi)],
                           None,
                           descr=descrs.jf_frame_info)
        self.newops.append(op2)
        arglist = op.getarglist()
        index_list = loop_token.compiled_loop_token._ll_initial_locs
        for i, arg in enumerate(arglist):
            descr = self.cpu.getarraydescr_for_frame(arg.type)
            assert self.cpu.JITFRAME_FIXED_SIZE & 1 == 0
            _, itemsize, _ = self.cpu.unpack_arraydescr_size(descr)
            index = index_list[i] // itemsize  # index is in bytes
            self.newops.append(
                ResOperation(rop.SETARRAYITEM_GC,
                             [frame, ConstInt(index), arg], None, descr))
        descr = op.getdescr()
        assert isinstance(descr, JitCellToken)
        jd = descr.outermost_jitdriver_sd
        args = [frame]
        if jd and jd.index_of_virtualizable >= 0:
            args = [frame, arglist[jd.index_of_virtualizable]]
        else:
            args = [frame]
        self.newops.append(
            ResOperation(rop.CALL_ASSEMBLER, args, op.result, op.getdescr()))

    # ----------

    def emitting_an_operation_that_can_collect(self):
        # must be called whenever we emit an operation that can collect:
        # forgets the previous MALLOC_NURSERY, if any; and empty the
        # set 'write_barrier_applied', so that future SETFIELDs will generate
        # a write barrier as usual.
        # it also writes down all the pending zero ptr fields
        self._op_malloc_nursery = None
        self.write_barrier_applied.clear()
        self.emit_pending_zeros()

    def emit_pending_zeros(self):
        # First, try to rewrite the existing ZERO_ARRAY operations from
        # the 'last_zero_arrays' list.  Note that these operation objects
        # are also already in 'newops', which is the point.
        for op in self.last_zero_arrays:
            assert op.getopnum() == rop.ZERO_ARRAY
            box = op.getarg(0)
            try:
                intset = self.setarrayitems_occurred[box]
            except KeyError:
                continue
            assert op.getarg(1).getint() == 0  # always 'start=0' initially
            start = 0
            while start in intset:
                start += 1
            op.setarg(1, ConstInt(start))
            stop = op.getarg(2).getint()
            assert start <= stop
            while stop > start and (stop - 1) in intset:
                stop -= 1
            op.setarg(2, ConstInt(stop - start))
            # ^^ may be ConstInt(0); then the operation becomes a no-op
        del self.last_zero_arrays[:]
        self.setarrayitems_occurred.clear()
        #
        # Then write the ZERO_PTR_FIELDs that are still pending
        for v, d in self.delayed_zero_setfields.iteritems():
            for ofs in d.iterkeys():
                op = ResOperation(rop.ZERO_PTR_FIELD, [v, ConstInt(ofs)], None)
                self.newops.append(op)
        self.delayed_zero_setfields.clear()

    def _gen_call_malloc_gc(self, args, v_result, descr):
        """Generate a CALL_MALLOC_GC with the given args."""
        self.emitting_an_operation_that_can_collect()
        op = ResOperation(rop.CALL_MALLOC_GC, args, v_result, descr)
        self.newops.append(op)
        # In general, don't add v_result to write_barrier_applied:
        # v_result might be a large young array.

    def gen_malloc_fixedsize(self, size, typeid, v_result):
        """Generate a CALL_MALLOC_GC(malloc_fixedsize_fn, ...).
        Used on Boehm, and on the framework GC for large fixed-size
        mallocs.  (For all I know this latter case never occurs in
        practice, but better safe than sorry.)
        """
        if self.gc_ll_descr.fielddescr_tid is not None:  # framework GC
            assert (size & (WORD - 1)) == 0, "size not aligned?"
            addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_big_fixedsize')
            args = [ConstInt(addr), ConstInt(size), ConstInt(typeid)]
            descr = self.gc_ll_descr.malloc_big_fixedsize_descr
        else:  # Boehm
            addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_fixedsize')
            args = [ConstInt(addr), ConstInt(size)]
            descr = self.gc_ll_descr.malloc_fixedsize_descr
        self._gen_call_malloc_gc(args, v_result, descr)
        # mark 'v_result' as freshly malloced, so not needing a write barrier
        # (this is always true because it's a fixed-size object)
        self.write_barrier_applied[v_result] = None

    def gen_boehm_malloc_array(self, arraydescr, v_num_elem, v_result):
        """Generate a CALL_MALLOC_GC(malloc_array_fn, ...) for Boehm."""
        addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_array')
        self._gen_call_malloc_gc([
            ConstInt(addr),
            ConstInt(arraydescr.basesize), v_num_elem,
            ConstInt(arraydescr.itemsize),
            ConstInt(arraydescr.lendescr.offset)
        ], v_result, self.gc_ll_descr.malloc_array_descr)

    def gen_malloc_array(self, arraydescr, v_num_elem, v_result):
        """Generate a CALL_MALLOC_GC(malloc_array_fn, ...) going either
        to the standard or the nonstandard version of the function."""
        #
        if (arraydescr.basesize == self.gc_ll_descr.standard_array_basesize
                and arraydescr.lendescr.offset
                == self.gc_ll_descr.standard_array_length_ofs):
            # this is a standard-looking array, common case
            addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_array')
            args = [
                ConstInt(addr),
                ConstInt(arraydescr.itemsize),
                ConstInt(arraydescr.tid), v_num_elem
            ]
            calldescr = self.gc_ll_descr.malloc_array_descr
        else:
            # rare case, so don't care too much about the number of arguments
            addr = self.gc_ll_descr.get_malloc_fn_addr(
                'malloc_array_nonstandard')
            args = [
                ConstInt(addr),
                ConstInt(arraydescr.basesize),
                ConstInt(arraydescr.itemsize),
                ConstInt(arraydescr.lendescr.offset),
                ConstInt(arraydescr.tid), v_num_elem
            ]
            calldescr = self.gc_ll_descr.malloc_array_nonstandard_descr
        self._gen_call_malloc_gc(args, v_result, calldescr)

    def gen_malloc_str(self, v_num_elem, v_result):
        """Generate a CALL_MALLOC_GC(malloc_str_fn, ...)."""
        addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_str')
        self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result,
                                 self.gc_ll_descr.malloc_str_descr)

    def gen_malloc_unicode(self, v_num_elem, v_result):
        """Generate a CALL_MALLOC_GC(malloc_unicode_fn, ...)."""
        addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_unicode')
        self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result,
                                 self.gc_ll_descr.malloc_unicode_descr)

    def gen_malloc_nursery_varsize(self,
                                   itemsize,
                                   v_length,
                                   v_result,
                                   arraydescr,
                                   kind=FLAG_ARRAY):
        """ itemsize is an int, v_length and v_result are boxes
        """
        gc_descr = self.gc_ll_descr
        if (kind == FLAG_ARRAY
                and (arraydescr.basesize != gc_descr.standard_array_basesize
                     or arraydescr.lendescr.offset !=
                     gc_descr.standard_array_length_ofs)):
            return False
        self.emitting_an_operation_that_can_collect()
        op = ResOperation(
            rop.CALL_MALLOC_NURSERY_VARSIZE,
            [ConstInt(kind), ConstInt(itemsize), v_length],
            v_result,
            descr=arraydescr)
        self.newops.append(op)
        # don't record v_result into self.write_barrier_applied:
        # it can be a large, young array with card marking, and then
        # the GC relies on the write barrier being called
        return True

    def gen_malloc_nursery_varsize_frame(self, sizebox, v_result):
        """ Generate CALL_MALLOC_NURSERY_VARSIZE_FRAME
        """
        self.emitting_an_operation_that_can_collect()
        op = ResOperation(rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME, [sizebox],
                          v_result)

        self.newops.append(op)
        self.write_barrier_applied[v_result] = None

    def gen_malloc_nursery(self, size, v_result):
        """Try to generate or update a CALL_MALLOC_NURSERY.
        If that succeeds, return True; you still need to write the tid.
        If that fails, return False.
        """
        size = self.round_up_for_allocation(size)
        if not self.gc_ll_descr.can_use_nursery_malloc(size):
            return False
        #
        op = None
        if self._op_malloc_nursery is not None:
            # already a MALLOC_NURSERY: increment its total size
            total_size = self._op_malloc_nursery.getarg(0).getint()
            total_size += size
            if self.gc_ll_descr.can_use_nursery_malloc(total_size):
                # if the total size is still reasonable, merge it
                self._op_malloc_nursery.setarg(0, ConstInt(total_size))
                op = ResOperation(rop.INT_ADD, [
                    self._v_last_malloced_nursery,
                    ConstInt(self._previous_size)
                ], v_result)
        if op is None:
            # if we failed to merge with a previous MALLOC_NURSERY, emit one
            self.emitting_an_operation_that_can_collect()
            op = ResOperation(rop.CALL_MALLOC_NURSERY, [ConstInt(size)],
                              v_result)
            self._op_malloc_nursery = op
        #
        self.newops.append(op)
        self._previous_size = size
        self._v_last_malloced_nursery = v_result
        self.write_barrier_applied[v_result] = None
        return True

    def gen_initialize_tid(self, v_newgcobj, tid):
        if self.gc_ll_descr.fielddescr_tid is not None:
            # produce a SETFIELD to initialize the GC header
            op = ResOperation(rop.SETFIELD_GC,
                              [v_newgcobj, ConstInt(tid)],
                              None,
                              descr=self.gc_ll_descr.fielddescr_tid)
            self.newops.append(op)

    def gen_initialize_len(self, v_newgcobj, v_length, arraylen_descr):
        # produce a SETFIELD to initialize the array length
        op = ResOperation(rop.SETFIELD_GC, [v_newgcobj, v_length],
                          None,
                          descr=arraylen_descr)
        self.newops.append(op)

    # ----------

    def handle_write_barrier_setfield(self, op):
        val = op.getarg(0)
        if val not in self.write_barrier_applied:
            v = op.getarg(1)
            if (isinstance(v, BoxPtr)
                    or (isinstance(v, ConstPtr)
                        and rgc.needs_write_barrier(v.value))):
                self.gen_write_barrier(val)
                #op = op.copy_and_change(rop.SETFIELD_RAW)
        self.newops.append(op)

    def handle_write_barrier_setarrayitem(self, op):
        val = op.getarg(0)
        if val not in self.write_barrier_applied:
            v = op.getarg(2)
            if (isinstance(v, BoxPtr)
                    or (isinstance(v, ConstPtr)
                        and rgc.needs_write_barrier(v.value))):
                self.gen_write_barrier_array(val, op.getarg(1))
                #op = op.copy_and_change(rop.SET{ARRAYITEM,INTERIORFIELD}_RAW)
        self.newops.append(op)

    handle_write_barrier_setinteriorfield = handle_write_barrier_setarrayitem

    def gen_write_barrier(self, v_base):
        write_barrier_descr = self.gc_ll_descr.write_barrier_descr
        args = [v_base]
        self.newops.append(
            ResOperation(rop.COND_CALL_GC_WB,
                         args,
                         None,
                         descr=write_barrier_descr))
        self.write_barrier_applied[v_base] = None

    def gen_write_barrier_array(self, v_base, v_index):
        write_barrier_descr = self.gc_ll_descr.write_barrier_descr
        if write_barrier_descr.has_write_barrier_from_array(self.cpu):
            # If we know statically the length of 'v', and it is not too
            # big, then produce a regular write_barrier.  If it's unknown or
            # too big, produce instead a write_barrier_from_array.
            LARGE = 130
            length = self.known_lengths.get(v_base, LARGE)
            if length >= LARGE:
                # unknown or too big: produce a write_barrier_from_array
                args = [v_base, v_index]
                self.newops.append(
                    ResOperation(rop.COND_CALL_GC_WB_ARRAY,
                                 args,
                                 None,
                                 descr=write_barrier_descr))
                # a WB_ARRAY is not enough to prevent any future write
                # barriers, so don't add to 'write_barrier_applied'!
                return
        # fall-back case: produce a write_barrier
        self.gen_write_barrier(v_base)

    def round_up_for_allocation(self, size):
        if not self.gc_ll_descr.round_up:
            return size
        if self.gc_ll_descr.translate_support_code:
            from rpython.rtyper.lltypesystem import llarena
            return llarena.round_up_for_allocation(
                size, self.gc_ll_descr.minimal_size_in_nursery)
        else:
            # non-translated: do it manually
            # assume that "self.gc_ll_descr.minimal_size_in_nursery" is 2 WORDs
            size = max(size, 2 * WORD)
            return (size + WORD - 1) & ~(WORD - 1)  # round up
Beispiel #19
0
 def test_stuff_followed_by_guard(self):
     boxes = [(InputArgInt(1), InputArgInt(0)),
              (InputArgInt(0), InputArgInt(1)),
              (InputArgInt(1), InputArgInt(1)),
              (InputArgInt(-1), InputArgInt(1)),
              (InputArgInt(1), InputArgInt(-1)),
              (ConstInt(1), InputArgInt(0)), (ConstInt(0), InputArgInt(1)),
              (ConstInt(1), InputArgInt(1)), (ConstInt(-1), InputArgInt(1)),
              (ConstInt(1), InputArgInt(-1)), (InputArgInt(1), ConstInt(0)),
              (InputArgInt(0), ConstInt(1)), (InputArgInt(1), ConstInt(1)),
              (InputArgInt(-1), ConstInt(1)),
              (InputArgInt(1), ConstInt(-1))]
     guards = [rop.GUARD_FALSE, rop.GUARD_TRUE]
     all = [
         rop.INT_EQ, rop.INT_NE, rop.INT_LE, rop.INT_LT, rop.INT_GT,
         rop.INT_GE, rop.UINT_GT, rop.UINT_LT, rop.UINT_LE, rop.UINT_GE
     ]
     for a, b in boxes:
         for guard in guards:
             for op in all:
                 i1 = ResOperation(rop.SAME_AS_I, [ConstInt(1)])
                 res = ResOperation(op, [a, b])
                 ops = [
                     i1,
                     res,
                     ResOperation(guard, [res], descr=BasicFailDescr()),
                     ResOperation(rop.FINISH, [ConstInt(0)],
                                  descr=BasicFinalDescr()),
                 ]
                 ops[-2].setfailargs([i1])
                 inputargs = [i for i in (a, b) if not isinstance(i, Const)]
                 looptoken = JitCellToken()
                 self.cpu.compile_loop(inputargs, ops, looptoken)
                 inputvalues = [box.getint() for box in inputargs]
                 deadframe = self.cpu.execute_token(looptoken, *inputvalues)
                 result = self.cpu.get_int_value(deadframe, 0)
                 expected = execute(self.cpu, None, op, None, a, b)
                 if guard == rop.GUARD_FALSE:
                     assert result == expected
                 else:
                     assert result != expected
Beispiel #20
0
 def emit_setfield(self, ptr, value, descr):
     ofs, size, sign = unpack_fielddescr(descr)
     self.emit_gc_store_or_indexed(None, ptr, ConstInt(0), value,
                                   size, 1, ofs)
Beispiel #21
0
    def test_compile_bridge_check_profile_info(self):
        py.test.skip("does not work, reinvestigate")

        class FakeProfileAgent(object):
            def __init__(self):
                self.functions = []

            def native_code_written(self, name, address, size):
                self.functions.append((name, address, size))

        self.cpu.profile_agent = agent = FakeProfileAgent()

        i0 = InputArgInt()
        i1 = InputArgInt()
        i2 = InputArgInt()
        targettoken = TargetToken()
        faildescr1 = BasicFailDescr(1)
        faildescr2 = BasicFailDescr(2)
        looptoken = JitCellToken()
        looptoken.number = 17

        class FakeString(object):
            def __init__(self, val):
                self.val = val

            def _get_str(self):
                return self.val

        operations = [
            ResOperation(rop.LABEL, [i0], None, descr=targettoken),
            ResOperation(rop.DEBUG_MERGE_POINT, [FakeString("hello"), 0, 0],
                         None),
            ResOperation(rop.INT_ADD, [i0, ConstInt(1)], i1),
            ResOperation(rop.INT_LE, [i1, ConstInt(9)], i2),
            ResOperation(rop.GUARD_TRUE, [i2], None, descr=faildescr1),
            ResOperation(rop.JUMP, [i1], None, descr=targettoken),
        ]
        inputargs = [i0]
        operations[-2].setfailargs([i1])
        self.cpu.compile_loop(inputargs, operations, looptoken)
        name, loopaddress, loopsize = agent.functions[0]
        assert name == "Loop # 17: hello (loop counter 0)"
        assert loopaddress <= looptoken._ll_loop_code
        assert loopsize >= 40  # randomish number

        i1b = InputArgInt()
        i3 = InputArgInt()
        bridge = [
            ResOperation(rop.INT_LE, [i1b, ConstInt(19)], i3),
            ResOperation(rop.GUARD_TRUE, [i3], None, descr=faildescr2),
            ResOperation(rop.DEBUG_MERGE_POINT, [FakeString("bye"), 0, 0],
                         None),
            ResOperation(rop.JUMP, [i1b], None, descr=targettoken),
        ]
        bridge[1].setfailargs([i1b])

        self.cpu.compile_bridge(faildescr1, [i1b], bridge, looptoken)
        name, address, size = agent.functions[1]
        assert name == "Bridge # 0: bye (loop counter 1)"
        # Would be exactly ==, but there are some guard failure recovery
        # stubs in-between
        assert address >= loopaddress + loopsize
        assert size >= 10  # randomish number

        deadframe = self.cpu.execute_token(looptoken, 2)
        fail = self.cpu.get_latest_descr(deadframe)
        assert fail.identifier == 2
        res = self.cpu.get_int_value(deadframe, 0)
        assert res == 20
Beispiel #22
0
 def gen_initialize_tid(self, v_newgcobj, tid):
     if self.gc_ll_descr.fielddescr_tid is not None:
         # produce a SETFIELD to initialize the GC header
         self.emit_setfield(v_newgcobj, ConstInt(tid),
                            descr=self.gc_ll_descr.fielddescr_tid)
Beispiel #23
0
    def test_calling_convention(self, monkeypatch):
        if WORD != 4:
            py.test.skip("32-bit only test")
        from rpython.jit.backend.x86.regloc import eax, edx
        from rpython.jit.backend.x86 import codebuf, callbuilder
        from rpython.jit.codewriter.effectinfo import EffectInfo
        from rpython.rlib.libffi import types, clibffi
        had_stdcall = hasattr(clibffi, 'FFI_STDCALL')
        if not had_stdcall:  # not running on Windows, but we can still test
            monkeypatch.setattr(clibffi, 'FFI_STDCALL', 12345, raising=False)
            monkeypatch.setattr(callbuilder, 'stdcall_or_cdecl', True)
        else:
            assert callbuilder.stdcall_or_cdecl
        #
        for real_ffi, reported_ffi in [
            (clibffi.FFI_DEFAULT_ABI, clibffi.FFI_DEFAULT_ABI),
            (clibffi.FFI_STDCALL, clibffi.FFI_DEFAULT_ABI),
            (clibffi.FFI_STDCALL, clibffi.FFI_STDCALL)
        ]:
            cpu = self.cpu
            mc = codebuf.MachineCodeBlockWrapper()
            mc.MOV_rs(eax.value, 4)  # argument 1
            mc.MOV_rs(edx.value, 40)  # argument 10
            mc.SUB_rr(eax.value, edx.value)  # return arg1 - arg10
            if real_ffi == clibffi.FFI_DEFAULT_ABI:
                mc.RET()
            else:
                mc.RET16_i(40)
            rawstart = mc.materialize(cpu, [])
            #
            calldescr = cpu._calldescr_dynamic_for_tests([types.slong] * 10,
                                                         types.slong)
            calldescr.get_call_conv = lambda: reported_ffi  # <==== hack
            # ^^^ we patch get_call_conv() so that the test also makes sense
            #     on Linux, because clibffi.get_call_conv() would always
            #     return FFI_DEFAULT_ABI on non-Windows platforms.
            funcbox = ConstInt(rawstart)
            i1 = InputArgInt()
            i2 = InputArgInt()
            c = ConstInt(-1)
            faildescr = BasicFailDescr(1)
            cz = ConstInt(0)
            # we must call it repeatedly: if the stack pointer gets increased
            # by 40 bytes by the STDCALL call, and if we don't expect it,
            # then we are going to get our stack emptied unexpectedly by
            # several repeated calls
            ops = [
                ResOperation(rop.CALL_RELEASE_GIL_I,
                             [cz, funcbox, i1, c, c, c, c, c, c, c, c, i2],
                             descr=calldescr),
                ResOperation(rop.GUARD_NOT_FORCED, [], descr=faildescr),
                ResOperation(rop.CALL_RELEASE_GIL_I,
                             [cz, funcbox, i1, c, c, c, c, c, c, c, c, i2],
                             descr=calldescr),
                ResOperation(rop.GUARD_NOT_FORCED, [], descr=faildescr),
                ResOperation(rop.CALL_RELEASE_GIL_I,
                             [cz, funcbox, i1, c, c, c, c, c, c, c, c, i2],
                             descr=calldescr),
                ResOperation(rop.GUARD_NOT_FORCED, [], descr=faildescr),
                ResOperation(rop.CALL_RELEASE_GIL_I,
                             [cz, funcbox, i1, c, c, c, c, c, c, c, c, i2],
                             descr=calldescr),
                ResOperation(rop.GUARD_NOT_FORCED, [], descr=faildescr),
            ]
            i3 = ops[0]
            i4 = ops[2]
            i5 = ops[4]
            i6 = ops[6]

            ops += [
                ResOperation(rop.GUARD_FALSE, [i3], descr=BasicFailDescr(0)),
                ResOperation(rop.FINISH, [], descr=BasicFinalDescr(1))
            ]
            ops[-2].setfailargs([i3, i4, i5, i6])
            ops[1].setfailargs([])
            ops[3].setfailargs([])
            ops[5].setfailargs([])
            ops[7].setfailargs([])
            looptoken = JitCellToken()
            self.cpu.compile_loop([i1, i2], ops, looptoken)

            deadframe = self.cpu.execute_token(looptoken, 123450, 123408)
            fail = self.cpu.get_latest_descr(deadframe)
            assert fail.identifier == 0
            assert self.cpu.get_int_value(deadframe, 0) == 42
            assert self.cpu.get_int_value(deadframe, 1) == 42
            assert self.cpu.get_int_value(deadframe, 2) == 42
            assert self.cpu.get_int_value(deadframe, 3) == 42
def test_get_deep_immutable_oplist():
    a = ConstInt(1)
    b = ConstInt(2)
    ops = [rop.ResOperation(rop.rop.INT_ADD, [a, b])]
    newops = rop.get_deep_immutable_oplist(ops)
    py.test.raises(TypeError, "newops.append('foobar')")
    py.test.raises(TypeError, "newops[0] = 'foobar'")
    py.test.raises(AssertionError, "newops[0].setarg(0, 'd')")
    py.test.raises(AssertionError, "newops[0].setdescr('foobar')")


VARI = rop.InputArgInt()
VARF = rop.InputArgFloat()
args = [
    (rop.rop.INT_SIGNEXT, [VARI, ConstInt(2)], {
        'from': INT_WORD,
        'to': 2,
        'cast_to': ('i', 2)
    }),
    (rop.rop.CAST_FLOAT_TO_INT, [VARF], {
        'from': 8,
        'to': 4
    }),
    (rop.rop.CAST_SINGLEFLOAT_TO_FLOAT, [VARI], {
        'from': 4,
        'to': 8
    }),
    (rop.rop.CAST_FLOAT_TO_SINGLEFLOAT, [VARF], {
        'from': 8,
        'to': 4
Beispiel #25
0
def test_bug_1():
    v1 = BoxInt()
    v2 = BoxInt()
    v3 = BoxInt()
    v4 = BoxInt()
    v5 = BoxInt()
    v6 = BoxInt()
    v7 = BoxInt()
    v8 = BoxInt()
    v9 = BoxInt()
    v10 = BoxInt()
    v11 = BoxInt()
    v12 = BoxInt()
    v13 = BoxInt()
    v14 = BoxInt()
    v15 = BoxInt()
    v16 = BoxInt()
    v17 = BoxInt()
    v18 = BoxInt()
    v19 = BoxInt()
    v20 = BoxInt()
    v21 = BoxInt()
    v22 = BoxInt()
    v23 = BoxInt()
    v24 = BoxInt()
    v25 = BoxInt()
    v26 = BoxInt()
    v27 = BoxInt()
    v28 = BoxInt()
    v29 = BoxInt()
    v30 = BoxInt()
    v31 = BoxInt()
    v32 = BoxInt()
    v33 = BoxInt()
    v34 = BoxInt()
    v35 = BoxInt()
    v36 = BoxInt()
    v37 = BoxInt()
    v38 = BoxInt()
    v39 = BoxInt()
    v40 = BoxInt()
    tmp41 = BoxInt()
    tmp42 = BoxInt()
    tmp43 = BoxInt()
    tmp44 = BoxInt()
    tmp45 = BoxInt()
    inputargs = [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10]
    operations = [
        ResOperation(rop.UINT_LT, [v6, ConstInt(0)], v11),
        ResOperation(rop.INT_AND, [v3, ConstInt(31)], tmp41),
        ResOperation(rop.INT_RSHIFT, [v3, tmp41], v12),
        ResOperation(rop.INT_NEG, [v2], v13),
        ResOperation(rop.INT_ADD, [v11, v7], v14),
        ResOperation(rop.INT_OR, [v3, v2], v15),
        ResOperation(rop.INT_OR, [v12, v12], v16),
        ResOperation(rop.INT_NE, [v2, v5], v17),
        ResOperation(rop.INT_AND, [v5, ConstInt(31)], tmp42),
        ResOperation(rop.UINT_RSHIFT, [v14, tmp42], v18),
        ResOperation(rop.INT_AND, [v14, ConstInt(31)], tmp43),
        ResOperation(rop.INT_LSHIFT, [ConstInt(7), tmp43], v19),
        ResOperation(rop.INT_NEG, [v19], v20),
        ResOperation(rop.INT_MOD, [v3, ConstInt(1)], v21),
        ResOperation(rop.UINT_GE, [v15, v1], v22),
        ResOperation(rop.INT_AND, [v16, ConstInt(31)], tmp44),
        ResOperation(rop.INT_LSHIFT, [v8, tmp44], v23),
        ResOperation(rop.INT_IS_TRUE, [v17], v24),
        ResOperation(rop.INT_AND, [v5, ConstInt(31)], tmp45),
        ResOperation(rop.INT_LSHIFT, [v14, tmp45], v25),
        ResOperation(rop.INT_LSHIFT, [v5, ConstInt(17)], v26),
        ResOperation(rop.INT_EQ, [v9, v15], v27),
        ResOperation(rop.INT_GE, [ConstInt(0), v6], v28),
        ResOperation(rop.INT_NEG, [v15], v29),
        ResOperation(rop.INT_NEG, [v22], v30),
        ResOperation(rop.INT_ADD, [v7, v16], v31),
        ResOperation(rop.UINT_LT, [v19, v19], v32),
        ResOperation(rop.INT_ADD, [v2, ConstInt(1)], v33),
        ResOperation(rop.INT_NEG, [v5], v34),
        ResOperation(rop.INT_ADD, [v17, v24], v35),
        ResOperation(rop.UINT_LT, [ConstInt(2), v16], v36),
        ResOperation(rop.INT_NEG, [v9], v37),
        ResOperation(rop.INT_GT, [v4, v11], v38),
        ResOperation(rop.INT_LT, [v27, v22], v39),
        ResOperation(rop.INT_NEG, [v27], v40),
        ResOperation(rop.GUARD_FALSE, [v1], None, descr=BasicFailDescr()),
    ]
    operations[-1].setfailargs([
        v40, v10, v36, v26, v13, v30, v21, v33, v18, v25, v31, v32, v28, v29,
        v35, v38, v20, v39, v34, v23, v37
    ])
    cpu = CPU(None, None)
    cpu.setup_once()
    looptoken = JitCellToken()
    cpu.compile_loop(inputargs, operations, looptoken)
    args = [17, -20, -6, 6, 1, 13, 13, 9, 49, 8]
    deadframe = cpu.execute_token(looptoken, *args)
    assert cpu.get_int_value(deadframe, 0) == 0
    assert cpu.get_int_value(deadframe, 1) == 8
    assert cpu.get_int_value(deadframe, 2) == 1
    assert cpu.get_int_value(deadframe, 3) == 131072
    assert cpu.get_int_value(deadframe, 4) == 20
    assert cpu.get_int_value(deadframe, 5) == -1
    assert cpu.get_int_value(deadframe, 6) == 0
    assert cpu.get_int_value(deadframe, 7) == -19
    assert cpu.get_int_value(deadframe, 8) == 6
    assert cpu.get_int_value(deadframe, 9) == 26
    assert cpu.get_int_value(deadframe, 10) == 12
    assert cpu.get_int_value(deadframe, 11) == 0
    assert cpu.get_int_value(deadframe, 12) == 0
    assert cpu.get_int_value(deadframe, 13) == 2
    assert cpu.get_int_value(deadframe, 14) == 2
    assert cpu.get_int_value(deadframe, 15) == 1
    assert cpu.get_int_value(deadframe, 16) == -57344
    assert cpu.get_int_value(deadframe, 17) == 1
    assert cpu.get_int_value(deadframe, 18) == -1
    if WORD == 4:
        assert cpu.get_int_value(deadframe, 19) == -2147483648
    elif WORD == 8:
        assert cpu.get_int_value(deadframe, 19) == 19327352832
    assert cpu.get_int_value(deadframe, 20) == -49
Beispiel #26
0
 def prepare_op_guard_no_exception(self, op, fcond):
     loc = self.make_sure_var_in_reg(ConstInt(self.cpu.pos_exception()))
     arglocs = self._prepare_guard(op, [loc])
     return arglocs
Beispiel #27
0
def test_bug_0():
    v1 = BoxInt()
    v2 = BoxInt()
    v3 = BoxInt()
    v4 = BoxInt()
    v5 = BoxInt()
    v6 = BoxInt()
    v7 = BoxInt()
    v8 = BoxInt()
    v9 = BoxInt()
    v10 = BoxInt()
    v11 = BoxInt()
    v12 = BoxInt()
    v13 = BoxInt()
    v14 = BoxInt()
    v15 = BoxInt()
    v16 = BoxInt()
    v17 = BoxInt()
    v18 = BoxInt()
    v19 = BoxInt()
    v20 = BoxInt()
    v21 = BoxInt()
    v22 = BoxInt()
    v23 = BoxInt()
    v24 = BoxInt()
    v25 = BoxInt()
    v26 = BoxInt()
    v27 = BoxInt()
    v28 = BoxInt()
    v29 = BoxInt()
    v30 = BoxInt()
    v31 = BoxInt()
    v32 = BoxInt()
    v33 = BoxInt()
    v34 = BoxInt()
    v35 = BoxInt()
    v36 = BoxInt()
    v37 = BoxInt()
    v38 = BoxInt()
    v39 = BoxInt()
    v40 = BoxInt()
    tmp41 = BoxInt()
    tmp42 = BoxInt()
    tmp43 = BoxInt()
    tmp44 = BoxInt()
    tmp45 = BoxInt()
    tmp46 = BoxInt()
    inputargs = [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10]
    operations = [
        ResOperation(rop.UINT_GT, [v3, ConstInt(-48)], v11),
        ResOperation(rop.INT_XOR, [v8, v1], v12),
        ResOperation(rop.INT_GT, [v6, ConstInt(-9)], v13),
        ResOperation(rop.INT_LE, [v13, v2], v14),
        ResOperation(rop.INT_LE, [v11, v5], v15),
        ResOperation(rop.UINT_GE, [v13, v13], v16),
        ResOperation(rop.INT_OR, [v9, ConstInt(-23)], v17),
        ResOperation(rop.INT_LT, [v10, v13], v18),
        ResOperation(rop.INT_OR, [v15, v5], v19),
        ResOperation(rop.INT_XOR, [v17, ConstInt(54)], v20),
        ResOperation(rop.INT_MUL, [v8, v10], v21),
        ResOperation(rop.INT_OR, [v3, v9], v22),
        ResOperation(rop.INT_AND, [v11, ConstInt(-4)], tmp41),
        ResOperation(rop.INT_OR, [tmp41, ConstInt(1)], tmp42),
        ResOperation(rop.INT_MOD, [v12, tmp42], v23),
        ResOperation(rop.INT_IS_TRUE, [v6], v24),
        ResOperation(rop.UINT_RSHIFT, [v15, ConstInt(6)], v25),
        ResOperation(rop.INT_OR, [ConstInt(-4), v25], v26),
        ResOperation(rop.INT_INVERT, [v8], v27),
        ResOperation(rop.INT_SUB, [ConstInt(-113), v11], v28),
        ResOperation(rop.INT_NEG, [v7], v29),
        ResOperation(rop.INT_NEG, [v24], v30),
        ResOperation(rop.INT_FLOORDIV, [v3, ConstInt(53)], v31),
        ResOperation(rop.INT_MUL, [v28, v27], v32),
        ResOperation(rop.INT_AND, [v18, ConstInt(-4)], tmp43),
        ResOperation(rop.INT_OR, [tmp43, ConstInt(1)], tmp44),
        ResOperation(rop.INT_MOD, [v26, tmp44], v33),
        ResOperation(rop.INT_OR, [v27, v19], v34),
        ResOperation(rop.UINT_LT, [v13, ConstInt(1)], v35),
        ResOperation(rop.INT_AND, [v21, ConstInt(31)], tmp45),
        ResOperation(rop.INT_RSHIFT, [v21, tmp45], v36),
        ResOperation(rop.INT_AND, [v20, ConstInt(31)], tmp46),
        ResOperation(rop.UINT_RSHIFT, [v4, tmp46], v37),
        ResOperation(rop.UINT_GT, [v33, ConstInt(-11)], v38),
        ResOperation(rop.INT_NEG, [v7], v39),
        ResOperation(rop.INT_GT, [v24, v32], v40),
        ResOperation(rop.GUARD_FALSE, [v1], None, descr=BasicFailDescr()),
    ]
    operations[-1].setfailargs(
        [v40, v36, v37, v31, v16, v34, v35, v23, v22, v29, v14, v39, v30, v38])
    cpu = CPU(None, None)
    cpu.setup_once()
    looptoken = JitCellToken()
    cpu.compile_loop(inputargs, operations, looptoken)
    args = [-13, 10, 10, 8, -8, -16, -18, 46, -12, 26]
    deadframe = cpu.execute_token(looptoken, *args)
    assert cpu.get_int_value(deadframe, 0) == 0
    assert cpu.get_int_value(deadframe, 1) == 0
    assert cpu.get_int_value(deadframe, 2) == 0
    assert cpu.get_int_value(deadframe, 3) == 0
    assert cpu.get_int_value(deadframe, 4) == 1
    assert cpu.get_int_value(deadframe, 5) == -7
    assert cpu.get_int_value(deadframe, 6) == 1
    assert cpu.get_int_value(deadframe, 7) == 0
    assert cpu.get_int_value(deadframe, 8) == -2
    assert cpu.get_int_value(deadframe, 9) == 18
    assert cpu.get_int_value(deadframe, 10) == 1
    assert cpu.get_int_value(deadframe, 11) == 18
    assert cpu.get_int_value(deadframe, 12) == -1
    assert cpu.get_int_value(deadframe, 13) == 0
Beispiel #28
0
 def transform_to_gc_load(self, op):
     NOT_SIGNED = 0
     CINT_ZERO = ConstInt(0)
     opnum = op.getopnum()
     if rop.is_getarrayitem(opnum) or \
        opnum in (rop.GETARRAYITEM_RAW_I,
                  rop.GETARRAYITEM_RAW_F):
         self.handle_getarrayitem(op)
     elif opnum in (rop.SETARRAYITEM_GC, rop.SETARRAYITEM_RAW):
         self.handle_setarrayitem(op)
     elif opnum == rop.RAW_STORE:
         itemsize, ofs, _ = unpack_arraydescr(op.getdescr())
         ptr_box = op.getarg(0)
         index_box = op.getarg(1)
         value_box = op.getarg(2)
         self.emit_gc_store_or_indexed(op, ptr_box, index_box, value_box,
                                       itemsize, 1, ofs)
     elif opnum in (rop.RAW_LOAD_I, rop.RAW_LOAD_F):
         itemsize, ofs, sign = unpack_arraydescr(op.getdescr())
         ptr_box = op.getarg(0)
         index_box = op.getarg(1)
         self.emit_gc_load_or_indexed(op, ptr_box, index_box, itemsize, 1,
                                      ofs, sign)
     elif opnum in (rop.GETINTERIORFIELD_GC_I, rop.GETINTERIORFIELD_GC_R,
                    rop.GETINTERIORFIELD_GC_F):
         ofs, itemsize, fieldsize, sign = unpack_interiorfielddescr(
             op.getdescr())
         ptr_box = op.getarg(0)
         index_box = op.getarg(1)
         self.emit_gc_load_or_indexed(op, ptr_box, index_box, fieldsize,
                                      itemsize, ofs, sign)
     elif opnum in (rop.SETINTERIORFIELD_RAW, rop.SETINTERIORFIELD_GC):
         ofs, itemsize, fieldsize, sign = unpack_interiorfielddescr(
             op.getdescr())
         ptr_box = op.getarg(0)
         index_box = op.getarg(1)
         value_box = op.getarg(2)
         self.emit_gc_store_or_indexed(op, ptr_box, index_box, value_box,
                                       fieldsize, itemsize, ofs)
     elif opnum in (rop.GETFIELD_GC_I, rop.GETFIELD_GC_F, rop.GETFIELD_GC_R,
                    rop.GETFIELD_RAW_I, rop.GETFIELD_RAW_F,
                    rop.GETFIELD_RAW_R):
         ofs, itemsize, sign = unpack_fielddescr(op.getdescr())
         ptr_box = op.getarg(0)
         if op.getopnum() in (rop.GETFIELD_GC_F, rop.GETFIELD_GC_I,
                              rop.GETFIELD_GC_R):
             # See test_zero_ptr_field_before_getfield().  We hope there is
             # no getfield_gc in the middle of initialization code, but there
             # shouldn't be, given that a 'new' is already delayed by previous
             # optimization steps.  In practice it should immediately be
             # followed by a bunch of 'setfields', and the 'pending_zeros'
             # optimization we do here is meant for this case.
             self.emit_pending_zeros()
             self.emit_gc_load_or_indexed(op, ptr_box, ConstInt(0),
                                          itemsize, 1, ofs, sign)
             self.emit_op(op)
             return True
         self.emit_gc_load_or_indexed(op, ptr_box, ConstInt(0), itemsize, 1,
                                      ofs, sign)
     elif opnum in (rop.SETFIELD_GC, rop.SETFIELD_RAW):
         ofs, itemsize, sign = unpack_fielddescr(op.getdescr())
         ptr_box = op.getarg(0)
         value_box = op.getarg(1)
         self.emit_gc_store_or_indexed(op, ptr_box, ConstInt(0), value_box,
                                       itemsize, 1, ofs)
     elif opnum == rop.ARRAYLEN_GC:
         descr = op.getdescr()
         assert isinstance(descr, ArrayDescr)
         ofs = descr.lendescr.offset
         self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD,
                                      1, ofs, NOT_SIGNED)
     elif opnum == rop.STRLEN:
         basesize, itemsize, ofs_length = get_array_token(
             rstr.STR, self.cpu.translate_support_code)
         self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD,
                                      1, ofs_length, NOT_SIGNED)
     elif opnum == rop.UNICODELEN:
         basesize, itemsize, ofs_length = get_array_token(
             rstr.UNICODE, self.cpu.translate_support_code)
         self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD,
                                      1, ofs_length, NOT_SIGNED)
     elif opnum == rop.STRHASH:
         offset, size = get_field_token(rstr.STR, 'hash',
                                        self.cpu.translate_support_code)
         assert size == WORD
         self.emit_gc_load_or_indexed(op,
                                      op.getarg(0),
                                      ConstInt(0),
                                      WORD,
                                      1,
                                      offset,
                                      sign=True)
     elif opnum == rop.UNICODEHASH:
         offset, size = get_field_token(rstr.UNICODE, 'hash',
                                        self.cpu.translate_support_code)
         assert size == WORD
         self.emit_gc_load_or_indexed(op,
                                      op.getarg(0),
                                      ConstInt(0),
                                      WORD,
                                      1,
                                      offset,
                                      sign=True)
     elif opnum == rop.STRGETITEM:
         basesize, itemsize, ofs_length = get_array_token(
             rstr.STR, self.cpu.translate_support_code)
         assert itemsize == 1
         basesize -= 1  # for the extra null character
         self.emit_gc_load_or_indexed(op, op.getarg(0), op.getarg(1),
                                      itemsize, itemsize, basesize,
                                      NOT_SIGNED)
     elif opnum == rop.UNICODEGETITEM:
         basesize, itemsize, ofs_length = get_array_token(
             rstr.UNICODE, self.cpu.translate_support_code)
         self.emit_gc_load_or_indexed(op, op.getarg(0), op.getarg(1),
                                      itemsize, itemsize, basesize,
                                      NOT_SIGNED)
     elif opnum == rop.STRSETITEM:
         basesize, itemsize, ofs_length = get_array_token(
             rstr.STR, self.cpu.translate_support_code)
         assert itemsize == 1
         basesize -= 1  # for the extra null character
         self.emit_gc_store_or_indexed(op, op.getarg(0), op.getarg(1),
                                       op.getarg(2), itemsize, itemsize,
                                       basesize)
     elif opnum == rop.UNICODESETITEM:
         basesize, itemsize, ofs_length = get_array_token(
             rstr.UNICODE, self.cpu.translate_support_code)
         self.emit_gc_store_or_indexed(op, op.getarg(0), op.getarg(1),
                                       op.getarg(2), itemsize, itemsize,
                                       basesize)
     return False
Beispiel #29
0
 def getstrlen(self, op, string_optimizer, mode):
     assert op is not None
     if self.lgtop is None:
         self.lgtop = ConstInt(len(self._chars))
     return self.lgtop
Beispiel #30
0
 def emit_getfield(self, ptr, descr, type='i', raw=False):
     ofs, size, sign = unpack_fielddescr(descr)
     op = self.emit_gc_load_or_indexed(None, ptr, ConstInt(0), size, 1, ofs,
                                       sign)
     return op
Beispiel #31
0
 def gen_malloc_str(self, v_num_elem, v_result):
     """Generate a CALL_R(malloc_str_fn, ...)."""
     addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_str')
     self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result,
                              self.gc_ll_descr.malloc_str_descr)
Beispiel #32
0
 def optimize_NEW_WITH_VTABLE(self, op):
     known_class = ConstInt(op.getdescr().get_vtable())
     self.make_virtual(known_class, op, op.getdescr())
Beispiel #33
0
 def cls_of_box(self, box):
     obj = lltype.cast_opaque_ptr(OBJECTPTR, box.getref_base())
     return ConstInt(ptr2int(obj.typeptr))