Exemplo n.º 1
0
    def test_quasiimmut_seen_consts(self):
        h = HeapCache()
        box1 = ConstPtr(rffi.cast(llmemory.GCREF, 1))
        box2 = ConstPtr(rffi.cast(llmemory.GCREF, 1))
        box3 = ConstPtr(rffi.cast(llmemory.GCREF, 1))
        box4 = ConstPtr(rffi.cast(llmemory.GCREF, 1))
        assert not h.is_quasi_immut_known(descr1, box1)
        assert not h.is_quasi_immut_known(descr1, box2)
        assert not h.is_quasi_immut_known(descr2, box3)
        assert not h.is_quasi_immut_known(descr2, box4)
        h.quasi_immut_now_known(descr1, box1)
        assert h.is_quasi_immut_known(descr1, box1)
        assert h.is_quasi_immut_known(descr1, box2)
        assert not h.is_quasi_immut_known(descr2, box3)
        assert not h.is_quasi_immut_known(descr2, box4)
        h.quasi_immut_now_known(descr2, box3)
        assert h.is_quasi_immut_known(descr1, box1)
        assert h.is_quasi_immut_known(descr1, box2)
        assert h.is_quasi_immut_known(descr2, box3)
        assert h.is_quasi_immut_known(descr2, box4)

        # invalidate the descr1 cache

        vbox1 = RefFrontendOp(1)
        vbox2 = RefFrontendOp(2)
        h.setfield(vbox1, vbox2, descr1)
        assert not h.is_quasi_immut_known(descr1, box1)
        assert not h.is_quasi_immut_known(descr1, box2)

        # a call invalidates everything
        h.invalidate_caches(
            rop.CALL_N, FakeCallDescr(FakeEffectinfo.EF_CAN_RAISE), [])
        assert not h.is_quasi_immut_known(descr2, box3)
        assert not h.is_quasi_immut_known(descr2, box4)
Exemplo n.º 2
0
 def test_replace_box_with_const(self):
     h = HeapCache()
     box1 = RefFrontendOp(1)
     box2 = RefFrontendOp(2)
     box3 = RefFrontendOp(3)
     c_box3 = ConstPtr(ConstPtr.value)
     h.setfield(box1, box2, descr1)
     h.setfield(box1, box3, descr2)
     h.setfield(box2, box3, descr3)
     h.replace_box(box3, c_box3)
     assert h.getfield(box1, descr1) is box2
     assert c_box3.same_constant(h.getfield(box1, descr2))
     assert c_box3.same_constant(h.getfield(box2, descr3))
Exemplo n.º 3
0
 def test_replace_box_with_const(self):
     h = HeapCache()
     box1 = RefFrontendOp(1)
     box2 = RefFrontendOp(2)
     box3 = RefFrontendOp(3)
     c_box3 = ConstPtr(ConstPtr.value)
     h.setfield(box1, box2, descr1)
     h.setfield(box1, box3, descr2)
     h.setfield(box2, box3, descr3)
     h.replace_box(box3, c_box3)
     assert h.getfield(box1, descr1) is box2
     assert c_box3.same_constant(h.getfield(box1, descr2))
     assert c_box3.same_constant(h.getfield(box2, descr3))
Exemplo n.º 4
0
def make_args_for_op(op, a, b):
    n = opname[op]
    if n[0:3] == 'INT' or n[0:4] == 'UINT':
        arg1 = ConstInt(a)
        arg2 = ConstInt(b)
    elif n[0:5] == 'FLOAT':
        arg1 = constfloat(float(a))
        arg2 = constfloat(float(b))
    elif n[0:3] == 'PTR':
        arg1 = ConstPtr(rffi.cast(llmemory.GCREF, a))
        arg2 = ConstPtr(rffi.cast(llmemory.GCREF, b))
    else:
        raise NotImplementedError("Don't know how to make args for " + n)
    return arg1, arg2
Exemplo n.º 5
0
    def test_record_constptrs(self):
        class MyFakeCPU(object):
            def cast_adr_to_int(self, adr):
                assert adr == "some fake address"
                return 43

        class MyFakeGCRefList(object):
            def get_address_of_gcref(self, s_gcref1):
                assert s_gcref1 == s_gcref
                return "some fake address"

        S = lltype.GcStruct('S')
        s = lltype.malloc(S)
        s_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, s)
        v_random_box = BoxPtr()
        v_result = BoxInt()
        operations = [
            ResOperation(rop.PTR_EQ,
                         [v_random_box, ConstPtr(s_gcref)], v_result),
        ]
        gc_ll_descr = self.gc_ll_descr
        gc_ll_descr.gcrefs = MyFakeGCRefList()
        gcrefs = []
        operations = get_deep_immutable_oplist(operations)
        operations2 = gc_ll_descr.rewrite_assembler(MyFakeCPU(), operations,
                                                    gcrefs)
        assert operations2 == operations
        assert gcrefs == [s_gcref]
Exemplo n.º 6
0
 def test_nonstandard_virtualizable_const(self):
     h = HeapCache()
     # rare but not impossible situation for some interpreters: we have a
     # *constant* nonstandard virtualizable
     c_box = ConstPtr(ConstPtr.value)
     h.nonstandard_virtualizables_now_known(c_box) # should not crash
     assert not h.is_known_nonstandard_virtualizable(c_box)
Exemplo n.º 7
0
Arquivo: gc.py Projeto: weijiwei/pypy
 def __init__(self, cpu, const_pointers):
     size = len(const_pointers)
     # check that there are any moving object (i.e. chaning pointers).
     # Otherwise there is no reason for an instance of this class.
     assert size > 0
     #
     # prepare GC array to hold the pointers that may change
     self.ptr_array = lltype.malloc(MovableObjectTracker.ptr_array_type,
                                    size)
     self.ptr_array_descr = cpu.arraydescrof(
         MovableObjectTracker.ptr_array_type)
     self.ptr_array_gcref = lltype.cast_opaque_ptr(llmemory.GCREF,
                                                   self.ptr_array)
     # use always the same ConstPtr to access the array
     # (easer to read JIT trace)
     self.const_ptr_gcref_array = ConstPtr(self.ptr_array_gcref)
     #
     # assign each pointer an index and put the pointer into the GC array.
     # as pointers and addresses are not a good key to use before translation
     # ConstPtrs are used as the key for the dict.
     self._indexes = {}
     for index in range(size):
         ptr = const_pointers[index]
         self._indexes[ptr] = index
         self.ptr_array[index] = ptr.value
Exemplo n.º 8
0
 def get_structptr_var(self,
                       r,
                       must_have_vtable=False,
                       type=lltype.Struct,
                       array_of_structs=False):
     while True:
         ptrvars = self._choose_ptr_vars(self.ptrvars, type,
                                         array_of_structs)
         if ptrvars and r.random() < 0.8:
             v, S = r.choice(ptrvars)
         else:
             prebuilt_ptr_consts = self._choose_ptr_vars(
                 self.prebuilt_ptr_consts, type, array_of_structs)
             if prebuilt_ptr_consts and r.random() < 0.7:
                 v, S = r.choice(prebuilt_ptr_consts)
             else:
                 if type is lltype.Struct:
                     # create a new constant structure
                     must_have_vtable = must_have_vtable or r.random() < 0.5
                     p = self.get_random_structure(
                         r, has_vtable=must_have_vtable)
                 else:
                     # create a new constant array
                     p = self.get_random_array(
                         r, must_be_array_of_structs=array_of_structs)
                 S = lltype.typeOf(p).TO
                 v = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, p))
                 self.prebuilt_ptr_consts.append(
                     (v, S, self.field_values(p)))
         if not (must_have_vtable and S._names[0] != 'parent'):
             break
     return v, S
Exemplo n.º 9
0
def test_ResumeDataLoopMemo_refs():
    memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
    const = ConstPtr(demo55o)
    tagged = memo.getconst(const)
    index, tagbits = untag(tagged)
    assert tagbits == TAGCONST
    assert memo.consts[index - TAG_CONST_OFFSET] is const
    tagged = memo.getconst(ConstPtr(demo55o))
    index2, tagbits = untag(tagged)
    assert tagbits == TAGCONST
    assert index2 == index
    tagged = memo.getconst(ConstPtr(demo66o))
    index3, tagbits = untag(tagged)
    assert tagbits == TAGCONST
    assert index3 != index
    tagged = memo.getconst(CONST_NULL)
    assert tagged == NULLREF
Exemplo n.º 10
0
def constbox(v):
    if v.type == INT:
        return ConstInt(getint(v))
    if v.type == FLOAT:
        return ConstFloat(getfloatstorage(v))
    if v.type == REF:
        return ConstPtr(getref_base(v))
    assert 0, v.type
Exemplo n.º 11
0
def constant_from_op(op):
    if op.type == 'i':
        return ConstInt(op.getint())
    elif op.type == 'r':
        return ConstPtr(op.getref_base())
    else:
        assert op.type == 'f'
        return ConstFloat(op.getfloatstorage())
Exemplo n.º 12
0
 def get_current_constant_fieldvalue(self):
     struct = self.struct
     fielddescr = self.fielddescr
     if self.fielddescr.is_pointer_field():
         return ConstPtr(self.cpu.bh_getfield_gc_r(struct, fielddescr))
     elif self.fielddescr.is_float_field():
         return ConstFloat(self.cpu.bh_getfield_gc_f(struct, fielddescr))
     else:
         return ConstInt(self.cpu.bh_getfield_gc_i(struct, fielddescr))
Exemplo n.º 13
0
    def test_known_class_generalization(self):
        knownclass1 = OptValue(BoxPtr())
        knownclass1.make_constant_class(ConstPtr(self.someptr1), 0)
        info1 = NotVirtualStateInfo(knownclass1)
        info1.position = 0
        knownclass2 = OptValue(BoxPtr())
        knownclass2.make_constant_class(ConstPtr(self.someptr1), 0)
        info2 = NotVirtualStateInfo(knownclass2)
        info2.position = 0
        assert info1.generalization_of(info2, {}, {})
        assert info2.generalization_of(info1, {}, {})

        knownclass3 = OptValue(BoxPtr())
        knownclass3.make_constant_class(ConstPtr(self.someptr2), 0)
        info3 = NotVirtualStateInfo(knownclass3)
        info3.position = 0
        assert not info1.generalization_of(info3, {}, {})
        assert not info2.generalization_of(info3, {}, {})
        assert not info3.generalization_of(info2, {}, {})
        assert not info3.generalization_of(info1, {}, {})
Exemplo n.º 14
0
    def test_arrayitems(self):
        TP = lltype.GcArray(lltype.Signed)
        ofs = symbolic.get_field_token(TP, 'length', False)[0]
        itemsofs = symbolic.get_field_token(TP, 'items', False)[0]
        descr = self.cpu.arraydescrof(TP)
        res = self.execute_operation(rop.NEW_ARRAY, [ConstInt(10)], 'ref',
                                     descr)
        resbuf = self._resbuf(res)
        assert resbuf[ofs / WORD] == 10
        self.execute_operation(
            rop.SETARRAYITEM_GC,
            [InputArgRef(res), ConstInt(2),
             InputArgInt(38)], 'void', descr)
        assert resbuf[itemsofs / WORD + 2] == 38

        self.execute_operation(
            rop.SETARRAYITEM_GC,
            [InputArgRef(res),
             InputArgInt(3), InputArgInt(42)], 'void', descr)
        assert resbuf[itemsofs / WORD + 3] == 42

        r = self.execute_operation(
            rop.GETARRAYITEM_GC_I,
            [InputArgRef(res), ConstInt(2)], 'int', descr)
        assert r == 38
        r = self.execute_operation(
            rop.GETARRAYITEM_GC_I,
            [ConstPtr(res), InputArgInt(2)], 'int', descr)
        assert r == 38
        r = self.execute_operation(rop.GETARRAYITEM_GC_I,
                                   [ConstPtr(res), ConstInt(2)], 'int', descr)
        assert r == 38
        r = self.execute_operation(
            rop.GETARRAYITEM_GC_I,
            [InputArgRef(res), InputArgInt(2)], 'int', descr)
        assert r == 38

        r = self.execute_operation(
            rop.GETARRAYITEM_GC_I,
            [InputArgRef(res), InputArgInt(3)], 'int', descr)
        assert r == 42
Exemplo n.º 15
0
def wrap_constant(value):
    if lltype.typeOf(value) == lltype.Signed:
        return ConstInt(value)
    elif isinstance(value, bool):
        return ConstInt(int(value))
    elif lltype.typeOf(value) == longlong.FLOATSTORAGE:
        return ConstFloat(value)
    elif isinstance(value, float):
        return ConstFloat(longlong.getfloatstorage(value))
    else:
        assert lltype.typeOf(value) == llmemory.GCREF
        return ConstPtr(value)
Exemplo n.º 16
0
 def gen_guard(self, builder, r):
     if r.random() < 0.5:
         return GuardClassOperation.gen_guard(self, builder, r)
     else:
         NULL = lltype.nullptr(llmemory.GCREF.TO)
         op = ResOperation(rop.SAME_AS_R, [ConstPtr(NULL)])
         builder.loop.operations.append(op)
         v2, S2 = builder.get_structptr_var(r, must_have_vtable=True)
         vtable2 = S2._hints['vtable']._as_ptr()
         c_vtable2 = ConstInt(ptr2int(vtable2))
         op = ResOperation(self.opnum, [op, c_vtable2], None)
         return op, False
Exemplo n.º 17
0
 def get_string(self, builder, r):
     current = getattr(builder, self.builder_cache)
     if current and r.random() < .8:
         v_string = r.choice(current)
         string = getref(self.ptr, v_string)
     else:
         string = self.alloc(getint(builder.get_index(500, r)))
         v_string = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, string))
         current.append(v_string)
     for i in range(len(string.chars)):
         char = r.random_integer() % self.max
         string.chars[i] = lltype.cast_primitive(self.primitive, char)
     return v_string
Exemplo n.º 18
0
 def gen_guard(self, builder, r):
     if r.random() < 0.5:
         return GuardClassOperation.gen_guard(self, builder, r)
     else:
         v = BoxPtr(lltype.nullptr(llmemory.GCREF.TO))
         op = ResOperation(rop.SAME_AS, [ConstPtr(v.value)], v)
         builder.loop.operations.append(op)
         v2, S2 = builder.get_structptr_var(r, must_have_vtable=True)
         vtable2 = S2._hints['vtable']._as_ptr()
         c_vtable2 = ConstAddr(llmemory.cast_ptr_to_adr(vtable2),
                               builder.cpu)
         op = ResOperation(self.opnum, [v, c_vtable2], None)
         return op, False
Exemplo n.º 19
0
    def test_NotVirtualStateInfo_generalization(self):
        def isgeneral(value1, value2):
            info1 = NotVirtualStateInfo(value1)
            info1.position = 0
            info2 = NotVirtualStateInfo(value2)
            info2.position = 0
            return info1.generalization_of(info2, {}, {})

        assert isgeneral(OptValue(BoxInt()), OptValue(ConstInt(7)))
        assert not isgeneral(OptValue(ConstInt(7)), OptValue(BoxInt()))

        ptr = OptValue(BoxPtr())
        nonnull = OptValue(BoxPtr())
        nonnull.make_nonnull(0)
        knownclass = OptValue(BoxPtr())
        knownclass.make_constant_class(ConstPtr(self.someptr1), 0)
        const = OptValue(BoxPtr)
        const.make_constant_class(ConstPtr(self.someptr1), 0)
        const.make_constant(ConstPtr(self.someptr1))
        inorder = [ptr, nonnull, knownclass, const]
        for i in range(len(inorder)):
            for j in range(i, len(inorder)):
                assert isgeneral(inorder[i], inorder[j])
                if i != j:
                    assert not isgeneral(inorder[j], inorder[i])

        value1 = OptValue(BoxInt())
        value2 = OptValue(BoxInt())
        value2.intbound.make_lt(IntBound(10, 10))
        assert isgeneral(value1, value2)
        assert not isgeneral(value2, value1)

        assert isgeneral(OptValue(ConstInt(7)), OptValue(ConstInt(7)))
        S = lltype.GcStruct('S')
        foo = lltype.malloc(S)
        fooref = lltype.cast_opaque_ptr(llmemory.GCREF, foo)
        assert isgeneral(OptValue(ConstPtr(fooref)),
                         OptValue(ConstPtr(fooref)))
Exemplo n.º 20
0
 def _untag(self, tagged):
     tag, v = untag(tagged)
     if tag == TAGBOX:
         return self._get(v)
     elif tag == TAGINT:
         return ConstInt(v + SMALL_INT_START)
     elif tag == TAGCONSTPTR:
         return ConstPtr(self.trace._refs[v])
     elif tag == TAGCONSTOTHER:
         if v & 1:
             return ConstFloat(self.trace._floats[v >> 1])
         else:
             return ConstInt(self.trace._bigints[v >> 1])
     else:
         assert False
Exemplo n.º 21
0
 def test_bug_setfield_64bit(self):
     if WORD == 4:
         py.test.skip("only for 64 bits")
     TP = lltype.GcStruct('S', ('i', lltype.Signed))
     ofsi = self.cpu.fielddescrof(TP, 'i')
     for i in range(500):
         p = lltype.malloc(TP)
         addr = rffi.cast(lltype.Signed, p)
         if fits_in_32bits(addr):
             break  # fitting in 32 bits, good
     else:
         py.test.skip("cannot get a 32-bit pointer")
     res = ConstPtr(rffi.cast(llmemory.GCREF, addr))
     self.execute_operation(rop.SETFIELD_RAW, [res, ConstInt(3**33)],
                            'void', ofsi)
     assert p.i == 3**33
Exemplo n.º 22
0
def test_simple_read():
    #b1, b2, b3 = [BoxInt(), InputArgRef(), BoxInt()]
    c1, c2, c3 = [ConstInt(111), ConstInt(222), ConstInt(333)]
    storage = Storage()
    storage.rd_consts = [c1, c2, c3]
    numb = Numbering(
        [
            3,
            tag(0, TAGBOX),
            tagconst(0), NULLREF,
            tag(0, TAGBOX),
            tag(1, TAGBOX)
        ] + [tagconst(1), tagconst(2)] +
        [tag(0, TAGBOX), tag(1, TAGBOX),
         tag(2, TAGBOX)])
    storage.rd_numb = numb
    #
    cpu = MyCPU([42, gcref1, -66])
    metainterp = MyMetaInterp(cpu)
    reader = ResumeDataDirectReader(metainterp, storage, "deadframe")
    _next_section(reader, 42, 111, gcrefnull, 42, gcref1)
    _next_section(reader, 222, 333)
    _next_section(reader, 42, gcref1, -66)
    #
    reader = ResumeDataBoxReader(storage, "deadframe", metainterp)
    bi, br, bf = [None] * 3, [None] * 2, [None] * 0
    bh = MyBlackholeInterp([
        lltype.Signed, lltype.Signed, llmemory.GCREF, lltype.Signed,
        llmemory.GCREF
    ])
    bh.fake_consume_boxes(reader, bi, br, bf)
    b1s = reader.liveboxes[0]
    b2s = reader.liveboxes[1]
    assert_same(bi, [b1s, ConstInt(111), b1s])
    assert_same(br, [ConstPtr(gcrefnull), b2s])
    bi, br, bf = [None] * 2, [None] * 0, [None] * 0
    bh = MyBlackholeInterp([lltype.Signed, lltype.Signed])
    bh.fake_consume_boxes(reader, bi, br, bf)
    assert_same(bi, [ConstInt(222), ConstInt(333)])
    bi, br, bf = [None] * 2, [None] * 1, [None] * 0
    bh = MyBlackholeInterp([lltype.Signed, llmemory.GCREF, lltype.Signed])
    bh.fake_consume_boxes(reader, bi, br, bf)
    b3s = reader.liveboxes[2]
    assert_same(bi, [b1s, b3s])
    assert_same(br, [b2s])
Exemplo n.º 23
0
def test_wrap():
    def _is(box1, box2):
        return (box1.__class__ == box2.__class__ and
                box1.value == box2.value)
    p = lltype.malloc(lltype.GcStruct('S'))
    po = lltype.cast_opaque_ptr(llmemory.GCREF, p)
    assert _is(wrap(None, 42), BoxInt(42))
    assert _is(wrap(None, 42.5), boxfloat(42.5))
    assert _is(wrap(None, p), BoxPtr(po))
    assert _is(wrap(None, 42, in_const_box=True), ConstInt(42))
    assert _is(wrap(None, 42.5, in_const_box=True), constfloat(42.5))
    assert _is(wrap(None, p, in_const_box=True), ConstPtr(po))
    if longlong.supports_longlong:
        import sys
        from rpython.rlib.rarithmetic import r_longlong, r_ulonglong
        value = r_longlong(-sys.maxint*17)
        assert _is(wrap(None, value), BoxFloat(value))
        assert _is(wrap(None, value, in_const_box=True), ConstFloat(value))
        value_unsigned = r_ulonglong(-sys.maxint*17)
        assert _is(wrap(None, value_unsigned), BoxFloat(value))
    sfval = r_singlefloat(42.5)
    ival = longlong.singlefloat2int(sfval)
    assert _is(wrap(None, sfval), BoxInt(ival))
    assert _is(wrap(None, sfval, in_const_box=True), ConstInt(ival))
Exemplo n.º 24
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

        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
            pypy_hooks.after_compile(di_loop)

        def interp_on_compile_bridge():
            pypy_hooks.after_compile_bridge(di_bridge)

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

        def interp_on_abort():
            pypy_hooks.on_abort(Counters.ABORT_TOO_LONG, pypyjitdriver,
                                greenkey, 'blah', Logger(MockSD), [])

        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.w_sorted_keys = space.wrap(sorted(Counters.counter_names))
Exemplo n.º 25
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
Exemplo n.º 26
0
 def test_constant_in_call_malloc(self):
     c = ConstPtr(rffi.cast(llmemory.GCREF, 0xdeadbeef1234))
     self.ensure_can_hold(rop.COND_CALL, [c], descr=self.calldescr)
     assert self.const_in_pool(c)
     assert self.const_in_pool(
         ConstPtr(rffi.cast(llmemory.GCREF, 0xdeadbeef1234)))
Exemplo n.º 27
0
def test_bug_2():
    cpu = CPU(None, None)
    cpu.setup_once()

    S4 = lltype.Struct('Sx', ("f0", lltype.Char), ("f1", lltype.Signed),
                       ("f2", lltype.Signed), ("f3", lltype.Signed))
    S5 = lltype.GcArray(S4)
    v1 = BoxInt()
    v2 = BoxInt()
    v3 = BoxInt()
    v4 = BoxInt()
    v5 = BoxInt()
    v6 = BoxInt()
    v7 = BoxInt()
    v8 = BoxInt()
    v9 = BoxInt()
    v10 = BoxInt()
    tmp11 = BoxInt()
    tmp12 = BoxPtr()
    faildescr0 = BasicFailDescr()
    tmp13 = BoxPtr()
    faildescr1 = BasicFailDescr()
    finishdescr2 = BasicFinalDescr()
    const_ptr14 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.STR, 1)))
    const_ptr15 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF,
                               lltype.malloc(rstr.UNICODE, 489)))
    const_ptr16 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE,
                                                             16)))
    const_ptr17 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S5, 299)))
    inputargs = [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10]

    xtp, func, funcdescr = getexception(cpu, 3)
    xtp2, func2, func2descr = getexception(cpu, 2)

    operations = [
        ResOperation(rop.STRGETITEM, [const_ptr14, ConstInt(0)], tmp11),
        ResOperation(rop.LABEL,
                     [v1, v2, tmp11, v3, v4, v5, v6, v7, v8, v9, v10], None,
                     TargetToken()),
        ResOperation(rop.UNICODESETITEM,
                     [const_ptr15, v4, ConstInt(22)], None),
        ResOperation(rop.CALL, [ConstInt(func), v2, v1, v9],
                     None,
                     descr=funcdescr),
        ResOperation(rop.GUARD_EXCEPTION, [ConstInt(xtp)],
                     tmp12,
                     descr=faildescr0),
        ResOperation(
            rop.UNICODESETITEM,
            [const_ptr16, ConstInt(13), ConstInt(9)], None),
        ResOperation(rop.SETINTERIORFIELD_GC, [const_ptr17, v3, v7], None,
                     cpu.interiorfielddescrof(S5, 'f3')),
        ResOperation(rop.CALL, [ConstInt(func2), v7, v10],
                     None,
                     descr=func2descr),
        ResOperation(rop.GUARD_NO_EXCEPTION, [], tmp13, descr=faildescr1),
        ResOperation(rop.FINISH, [], None, descr=finishdescr2),
    ]
    operations[4].setfailargs([v4, v8, v10, v2, v9, v7, v6, v1])
    operations[8].setfailargs([v3, v9, v2, v6, v4])
    looptoken = JitCellToken()
    cpu.compile_loop(inputargs, operations, looptoken)
    loop_args = [1, -39, 46, 21, 16, 6, -4611686018427387905, 12, 14, 2]
    frame = cpu.execute_token(looptoken, *loop_args)
    assert cpu.get_int_value(frame, 0) == 46
    assert cpu.get_int_value(frame, 1) == 14
    assert cpu.get_int_value(frame, 2) == -39
    assert cpu.get_int_value(frame, 3) == 6
    assert cpu.get_int_value(frame, 4) == 21
    S4 = lltype.GcStruct('Sx', ("parent", rclass.OBJECT),
                         ("f0", lltype.Signed))
    S5 = lltype.GcStruct('Sx', ("f0", lltype.Signed))
    S6 = lltype.GcArray(lltype.Signed)
    S7 = lltype.GcStruct('Sx', ("parent", rclass.OBJECT), ("f0", lltype.Char))
    S8 = lltype.Struct('Sx', ("f0", lltype.Char), ("f1", lltype.Signed),
                       ("f2", lltype.Signed), ("f3", lltype.Signed))
    S9 = lltype.GcArray(S8)
    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()
    p20 = BoxPtr()
    tmp21 = BoxPtr()
    faildescr3 = BasicFailDescr()
    tmp22 = BoxPtr()
    faildescr4 = BasicFailDescr()
    tmp23 = BoxInt()
    tmp24 = BoxInt()
    tmp25 = BoxInt()
    tmp26 = BoxInt()
    tmp27 = BoxInt()
    tmp28 = BoxInt()
    tmp29 = BoxInt()
    faildescr5 = BasicFailDescr()
    tmp30 = BoxPtr()
    faildescr6 = BasicFailDescr()
    finishdescr7 = BasicFinalDescr()
    const_ptr31 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S4)))
    const_ptr32 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.STR, 46)))
    const_ptr33 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S5)))
    const_ptr34 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE,
                                                             26)))
    const_ptr35 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE,
                                                             15)))
    const_ptr36 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S7)))
    const_ptr37 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.STR, 484)))
    const_ptr38 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S9, 299)))
    inputargs = [v1, v2, v3, v4, v5]

    func3, func3descr = getnoexception(cpu, 5)
    xtp3, func4, func4descr = getexception(cpu, 10)

    operations = [
        ResOperation(rop.GUARD_EXCEPTION, [ConstInt(xtp2)],
                     tmp21,
                     descr=faildescr3),
        ResOperation(rop.INT_IS_ZERO, [v4], v6),
        ResOperation(rop.INT_NE, [v6, ConstInt(13)], v7),
        ResOperation(rop.GETFIELD_GC, [const_ptr31], v8,
                     cpu.fielddescrof(S4, 'f0')),
        ResOperation(rop.STRSETITEM,
                     [const_ptr32, v6, ConstInt(0)], None),
        ResOperation(rop.NEWSTR, [ConstInt(5)], tmp22),
        ResOperation(rop.STRSETITEM,
                     [tmp22, ConstInt(0), ConstInt(42)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp22, ConstInt(1), ConstInt(42)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp22, ConstInt(2), ConstInt(20)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp22, ConstInt(3), ConstInt(48)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp22, ConstInt(4), ConstInt(6)], None),
        ResOperation(rop.GETFIELD_GC, [const_ptr33], v9,
                     cpu.fielddescrof(S5, 'f0')),
        ResOperation(rop.UNICODESETITEM,
                     [const_ptr34, ConstInt(24),
                      ConstInt(65533)], None),
        ResOperation(rop.GETFIELD_GC, [const_ptr31], v10,
                     cpu.fielddescrof(S4, 'f0')),
        ResOperation(rop.INT_NE, [v10, ConstInt(25)], v11),
        ResOperation(rop.CALL, [ConstInt(func3), v5, v1, v8, v3, v2],
                     v12,
                     descr=func3descr),
        ResOperation(rop.GUARD_NO_EXCEPTION, [], None, descr=faildescr4),
        ResOperation(rop.UNICODELEN, [const_ptr35], tmp23),
        ResOperation(rop.NEW_ARRAY, [v2], p20, cpu.arraydescrof(S6)),
        ResOperation(rop.GETFIELD_GC, [const_ptr36], v13,
                     cpu.fielddescrof(S7, 'f0')),
        ResOperation(rop.INT_OR, [v8, ConstInt(2)], tmp24),
        ResOperation(rop.INT_FLOORDIV, [ConstInt(8), tmp24], v14),
        ResOperation(rop.GETARRAYITEM_GC, [p20, ConstInt(3)], v15,
                     cpu.arraydescrof(S6)),
        ResOperation(
            rop.COPYSTRCONTENT,
            [tmp22, const_ptr37,
             ConstInt(1),
             ConstInt(163),
             ConstInt(0)], None),
        ResOperation(rop.COPYUNICODECONTENT,
                     [const_ptr35, const_ptr34,
                      ConstInt(13),
                      ConstInt(0), v6], None),
        ResOperation(rop.STRGETITEM, [tmp22, v6], tmp25),
        ResOperation(rop.STRGETITEM, [tmp22, ConstInt(0)], tmp26),
        ResOperation(rop.GETINTERIORFIELD_GC, [const_ptr38, v13], v16,
                     cpu.interiorfielddescrof(S9, 'f0')),
        ResOperation(rop.INT_GE, [v4, v5], v17),
        ResOperation(rop.INT_OR, [v13, ConstInt(2)], tmp27),
        ResOperation(rop.INT_FLOORDIV, [ConstInt(12), tmp27], v18),
        ResOperation(rop.INT_AND, [v1, ConstInt(-4)], tmp28),
        ResOperation(rop.INT_OR, [tmp28, ConstInt(2)], tmp29),
        ResOperation(rop.INT_FLOORDIV, [v15, tmp29], v19),
        ResOperation(rop.GUARD_FALSE, [v17], None, descr=faildescr5),
        ResOperation(rop.UNICODESETITEM,
                     [const_ptr34, ConstInt(20),
                      ConstInt(65522)], None),
        ResOperation(
            rop.CALL,
            [ConstInt(func4), v3, v9, v10, v8, v11, v5, v13, v14, v15, v6],
            None,
            descr=func4descr),
        ResOperation(rop.GUARD_NO_EXCEPTION, [], tmp30, descr=faildescr6),
        ResOperation(rop.FINISH, [], None, descr=finishdescr7),
    ]
    operations[0].setfailargs([])
    operations[16].setfailargs([v5, v9])
    operations[34].setfailargs([])
    operations[37].setfailargs([v12, v19, v10, v7, v4, v8, v18, v15, v9])
    cpu.compile_bridge(faildescr1, inputargs, operations, looptoken)
    frame = cpu.execute_token(looptoken, *loop_args)
    #assert cpu.get_int_value(frame, 0) == -9223372036854775766
    assert cpu.get_int_value(frame, 1) == 0
    #assert cpu.get_int_value(frame, 2) == -9223372036854775808
    assert cpu.get_int_value(frame, 3) == 1
    assert cpu.get_int_value(frame, 4) == 6
    #assert cpu.get_int_value(frame, 5) == -9223372036854775808
    assert cpu.get_int_value(frame, 6) == 0
    assert cpu.get_int_value(frame, 7) == 0
    #assert cpu.get_int_value(frame, 8) == 26
    S4 = lltype.GcStruct('Sx', ("parent", rclass.OBJECT),
                         ("f0", lltype.Signed), ("f1", lltype.Signed))
    S5 = lltype.GcStruct('Sx', ("parent", rclass.OBJECT),
                         ("f0", lltype.Signed))
    S6 = lltype.GcStruct('Sx', ("f0", lltype.Signed), ("f1", rffi.UCHAR))
    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()
    tmp19 = BoxPtr()
    faildescr8 = BasicFailDescr()
    tmp20 = BoxInt()
    tmp21 = BoxInt()
    tmp22 = BoxInt()
    tmp23 = BoxInt()
    faildescr9 = BasicFailDescr()
    tmp24 = BoxInt()
    tmp25 = BoxInt()
    tmp26 = BoxInt()
    tmp27 = BoxPtr()
    tmp28 = BoxPtr()
    faildescr10 = BasicFailDescr()
    finishdescr11 = BasicFinalDescr()
    const_ptr29 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S4)))
    const_ptr30 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE,
                                                             26)))
    const_ptr31 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE, 1)))
    const_ptr32 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S5)))
    const_ptr33 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S6)))
    const_ptr34 = ConstPtr(
        lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.STR, 26)))
    inputargs = [v1, v2, v3, v4, v5, v6, v7, v8, v9]
    operations = [
        ResOperation(rop.GUARD_EXCEPTION, [ConstInt(xtp3)],
                     tmp19,
                     descr=faildescr8),
        ResOperation(rop.SETFIELD_GC, [const_ptr29, v7], None,
                     cpu.fielddescrof(S4, 'f0')),
        ResOperation(rop.UNICODEGETITEM,
                     [const_ptr30, ConstInt(21)], tmp20),
        ResOperation(rop.UNICODEGETITEM,
                     [const_ptr30, ConstInt(10)], tmp21),
        ResOperation(rop.UINT_RSHIFT, [v9, ConstInt(40)], v10),
        ResOperation(rop.UNICODEGETITEM,
                     [const_ptr30, ConstInt(25)], tmp22),
        ResOperation(rop.INT_NE, [ConstInt(-8), v9], v11),
        ResOperation(rop.INT_MUL_OVF, [v3, ConstInt(-4)], tmp23),
        ResOperation(rop.GUARD_OVERFLOW, [], None, descr=faildescr9),
        ResOperation(rop.UNICODESETITEM,
                     [const_ptr31, ConstInt(0),
                      ConstInt(50175)], None),
        ResOperation(rop.UINT_GT, [v8, ConstInt(-6)], v12),
        ResOperation(rop.GETFIELD_GC, [const_ptr32], v13,
                     cpu.fielddescrof(S5, 'f0')),
        ResOperation(rop.INT_AND, [ConstInt(8), v8], v14),
        ResOperation(rop.INT_INVERT, [v1], v15),
        ResOperation(rop.SETFIELD_GC, [const_ptr33, ConstInt(3)], None,
                     cpu.fielddescrof(S6, 'f1')),
        ResOperation(rop.INT_GE, [v14, v6], v16),
        ResOperation(rop.INT_AND, [v5, ConstInt(-4)], tmp24),
        ResOperation(rop.INT_OR, [tmp24, ConstInt(2)], tmp25),
        ResOperation(rop.INT_FLOORDIV, [v9, tmp25], v17),
        ResOperation(rop.STRLEN, [const_ptr34], tmp26),
        ResOperation(rop.NEWSTR, [ConstInt(7)], tmp27),
        ResOperation(rop.STRSETITEM,
                     [tmp27, ConstInt(0), ConstInt(21)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp27, ConstInt(1), ConstInt(79)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp27, ConstInt(2), ConstInt(7)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp27, ConstInt(3), ConstInt(2)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp27, ConstInt(4), ConstInt(229)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp27, ConstInt(5), ConstInt(233)], None),
        ResOperation(rop.STRSETITEM,
                     [tmp27, ConstInt(6), ConstInt(208)], None),
        ResOperation(rop.INT_LT, [ConstInt(-31), v10], v18),
        ResOperation(rop.SAME_AS,
                     [ConstPtr(lltype.nullptr(llmemory.GCREF.TO))], tmp28),
        ResOperation(rop.GUARD_NONNULL_CLASS, [tmp28, ConstInt(xtp2)],
                     None,
                     descr=faildescr10),
        ResOperation(rop.FINISH, [v4], None, descr=finishdescr11),
    ]
    operations[0].setfailargs([])
    operations[8].setfailargs([tmp23, v5, v3, v11, v6])
    operations[30].setfailargs([v6])
    cpu.compile_bridge(faildescr6, inputargs, operations, looptoken)
    frame = cpu.execute_token(looptoken, *loop_args)
    #assert cpu.get_int_value(frame, 0) == -9223372036854775808
    v1 = BoxInt()
    v2 = BoxInt()
    p3 = BoxPtr()
    tmp4 = BoxInt()
    tmp5 = BoxPtr()
    faildescr12 = BasicFailDescr()
    finishdescr13 = BasicFinalDescr()
    inputargs = [v1]

    _, func5, func5descr = getexception(cpu, 0)
    vt = getvtable(cpu, S4)

    operations = [
        ResOperation(rop.INT_AND, [v1, ConstInt(63)], tmp4),
        ResOperation(rop.INT_LSHIFT, [ConstInt(10), tmp4], v2),
        ResOperation(rop.NEW_WITH_VTABLE, [ConstInt(vt)], p3),
        ResOperation(rop.CALL, [ConstInt(func5)], None, descr=func5descr),
        ResOperation(rop.GUARD_EXCEPTION, [ConstInt(xtp2)],
                     tmp5,
                     descr=faildescr12),
        ResOperation(rop.FINISH, [], None, descr=finishdescr13),
    ]
    operations[4].setfailargs([v2])
    cpu.compile_bridge(faildescr10, inputargs, operations, looptoken)
    frame = cpu.execute_token(looptoken, *loop_args)
Exemplo n.º 28
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