示例#1
0
def test_get_current_qmut_instance():
    accessor = FieldListAccessor()
    accessor.initialize(None, {'inst_x': IR_QUASIIMMUTABLE})
    STRUCT = lltype.GcStruct('Foo', ('inst_x', lltype.Signed),
                             ('mutate_x', rclass.OBJECTPTR),
                             hints={'immutable_fields': accessor})
    foo = lltype.malloc(STRUCT, zero=True)
    foo.inst_x = 42
    assert not foo.mutate_x

    class FakeCPU:
        ts = typesystem.llhelper

        def bh_getfield_gc_r(self, gcref, fielddescr):
            assert fielddescr == mutatefielddescr
            foo = lltype.cast_opaque_ptr(lltype.Ptr(STRUCT), gcref)
            result = foo.mutate_x
            return lltype.cast_opaque_ptr(llmemory.GCREF, result)

        def bh_setfield_gc_r(self, gcref, fielddescr, newvalue_gcref):
            assert fielddescr == mutatefielddescr
            foo = lltype.cast_opaque_ptr(lltype.Ptr(STRUCT), gcref)
            newvalue = lltype.cast_opaque_ptr(rclass.OBJECTPTR, newvalue_gcref)
            foo.mutate_x = newvalue

    cpu = FakeCPU()
    mutatefielddescr = ('fielddescr', STRUCT, 'mutate_x')

    foo_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, foo)
    qmut1 = get_current_qmut_instance(cpu, foo_gcref, mutatefielddescr)
    assert isinstance(qmut1, QuasiImmut)
    qmut2 = get_current_qmut_instance(cpu, foo_gcref, mutatefielddescr)
    assert qmut1 is qmut2
示例#2
0
def test_get_current_qmut_instance():
    accessor = FieldListAccessor()
    accessor.initialize(None, {'inst_x': IR_QUASIIMMUTABLE})
    STRUCT = lltype.GcStruct('Foo', ('inst_x', lltype.Signed),
                             ('mutate_x', rclass.OBJECTPTR),
                             hints={'immutable_fields': accessor})
    foo = lltype.malloc(STRUCT, zero=True)
    foo.inst_x = 42
    assert not foo.mutate_x

    class FakeCPU:
        ts = typesystem.llhelper

        def bh_getfield_gc_r(self, gcref, fielddescr):
            assert fielddescr == mutatefielddescr
            foo = lltype.cast_opaque_ptr(lltype.Ptr(STRUCT), gcref)
            result = foo.mutate_x
            return lltype.cast_opaque_ptr(llmemory.GCREF, result)

        def bh_setfield_gc_r(self, gcref, fielddescr, newvalue_gcref):
            assert fielddescr == mutatefielddescr
            foo = lltype.cast_opaque_ptr(lltype.Ptr(STRUCT), gcref)
            newvalue = lltype.cast_opaque_ptr(rclass.OBJECTPTR, newvalue_gcref)
            foo.mutate_x = newvalue

    cpu = FakeCPU()
    mutatefielddescr = ('fielddescr', STRUCT, 'mutate_x')

    foo_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, foo)
    qmut1 = get_current_qmut_instance(cpu, foo_gcref, mutatefielddescr)
    assert isinstance(qmut1, QuasiImmut)
    qmut2 = get_current_qmut_instance(cpu, foo_gcref, mutatefielddescr)
    assert qmut1 is qmut2
示例#3
0
def test_quasi_immutable_setfield():
    from pypy.rpython.rclass import FieldListAccessor, IR_QUASIIMMUTABLE
    accessor = FieldListAccessor()
    accessor.initialize(None, {'inst_x': IR_QUASIIMMUTABLE})
    v1 = varoftype(lltype.Signed)
    STRUCT = lltype.GcStruct('struct', ('inst_x', lltype.Signed),
                             ('mutate_x', rclass.OBJECTPTR),
                             hints={'immutable_fields': accessor})
    for v_x in [const(lltype.malloc(STRUCT)), varoftype(lltype.Ptr(STRUCT))]:
        op = SpaceOperation('jit_force_quasi_immutable',
                            [v_x, Constant('mutate_x', lltype.Void)],
                            varoftype(lltype.Void))
        tr = Transformer(FakeCPU(), FakeRegularCallControl())
        tr.graph = 'currentgraph'
        op0, op1 = tr.rewrite_operation(op)
        assert op0.opname == '-live-'
        assert op1.opname == 'jit_force_quasi_immutable'
        assert op1.args[0] == v_x
        assert op1.args[1] == ('fielddescr', STRUCT, 'mutate_x')
示例#4
0
def test_quasi_immutable_setfield():
    from pypy.rpython.rclass import FieldListAccessor, IR_QUASIIMMUTABLE
    accessor = FieldListAccessor()
    accessor.initialize(None, {'inst_x': IR_QUASIIMMUTABLE})
    v1 = varoftype(lltype.Signed)
    STRUCT = lltype.GcStruct('struct', ('inst_x', lltype.Signed),
                             ('mutate_x', rclass.OBJECTPTR),
                             hints={'immutable_fields': accessor})
    for v_x in [const(lltype.malloc(STRUCT)), varoftype(lltype.Ptr(STRUCT))]:
        op = SpaceOperation('jit_force_quasi_immutable',
                            [v_x, Constant('mutate_x', lltype.Void)],
                            varoftype(lltype.Void))
        tr = Transformer(FakeCPU(), FakeRegularCallControl())
        tr.graph = 'currentgraph'
        op0, op1 = tr.rewrite_operation(op)
        assert op0.opname == '-live-'
        assert op1.opname == 'jit_force_quasi_immutable'
        assert op1.args[0] == v_x
        assert op1.args[1] == ('fielddescr', STRUCT, 'mutate_x')
示例#5
0
 def __init__(self, rtyper, classdef):
     self._super().__init__(rtyper, classdef)
     classdesc = classdef.classdesc
     if '_virtualizable2_' in classdesc.classdict:
         basedesc = classdesc.basedesc
         assert basedesc is None or basedesc.lookup(
             '_virtualizable2_') is None
         self.top_of_virtualizable_hierarchy = True
         self.accessor = FieldListAccessor()
     else:
         self.top_of_virtualizable_hierarchy = False
示例#6
0
def test_quasi_immutable_setfield():
    from pypy.rpython.rclass import FieldListAccessor, IR_QUASIIMMUTABLE

    accessor = FieldListAccessor()
    accessor.initialize(None, {"inst_x": IR_QUASIIMMUTABLE})
    v1 = varoftype(lltype.Signed)
    STRUCT = lltype.GcStruct(
        "struct", ("inst_x", lltype.Signed), ("mutate_x", rclass.OBJECTPTR), hints={"immutable_fields": accessor}
    )
    for v_x in [const(lltype.malloc(STRUCT)), varoftype(lltype.Ptr(STRUCT))]:
        op = SpaceOperation(
            "jit_force_quasi_immutable", [v_x, Constant("mutate_x", lltype.Void)], varoftype(lltype.Void)
        )
        tr = Transformer(FakeCPU(), FakeRegularCallControl())
        tr.graph = "currentgraph"
        op0, op1 = tr.rewrite_operation(op)
        assert op0.opname == "-live-"
        assert op1.opname == "jit_force_quasi_immutable"
        assert op1.args[0] == v_x
        assert op1.args[1] == ("fielddescr", STRUCT, "mutate_x")
示例#7
0
def test_quasi_immutable():
    from pypy.rpython.rclass import FieldListAccessor, IR_QUASIIMMUTABLE
    accessor = FieldListAccessor()
    accessor.initialize(None, {'inst_x': IR_QUASIIMMUTABLE})
    v2 = varoftype(lltype.Signed)
    STRUCT = lltype.GcStruct('struct', ('inst_x', lltype.Signed),
                             ('mutate_x', rclass.OBJECTPTR),
                             hints={'immutable_fields': accessor})
    for v_x in [const(lltype.malloc(STRUCT)), varoftype(lltype.Ptr(STRUCT))]:
        op = SpaceOperation('getfield', [v_x, Constant('inst_x', lltype.Void)],
                            v2)
        tr = Transformer(FakeCPU())
        [_, op1, op2] = tr.rewrite_operation(op)
        assert op1.opname == 'record_quasiimmut_field'
        assert len(op1.args) == 3
        assert op1.args[0] == v_x
        assert op1.args[1] == ('fielddescr', STRUCT, 'inst_x')
        assert op1.args[2] == ('fielddescr', STRUCT, 'mutate_x')
        assert op1.result is None
        assert op2.opname == 'getfield_gc_i'
        assert len(op2.args) == 2
        assert op2.args[0] == v_x
        assert op2.args[1] == ('fielddescr', STRUCT, 'inst_x')
        assert op2.result is op.result
示例#8
0
def test_quasi_immutable():
    from pypy.rpython.rclass import FieldListAccessor, IR_QUASIIMMUTABLE
    accessor = FieldListAccessor()
    accessor.initialize(None, {'inst_x': IR_QUASIIMMUTABLE})
    v2 = varoftype(lltype.Signed)
    STRUCT = lltype.GcStruct('struct', ('inst_x', lltype.Signed),
                             ('mutate_x', rclass.OBJECTPTR),
                             hints={'immutable_fields': accessor})
    for v_x in [const(lltype.malloc(STRUCT)), varoftype(lltype.Ptr(STRUCT))]:
        op = SpaceOperation('getfield',
                            [v_x, Constant('inst_x', lltype.Void)], v2)
        tr = Transformer(FakeCPU())
        [_, op1, op2] = tr.rewrite_operation(op)
        assert op1.opname == 'record_quasiimmut_field'
        assert len(op1.args) == 3
        assert op1.args[0] == v_x
        assert op1.args[1] == ('fielddescr', STRUCT, 'inst_x')
        assert op1.args[2] == ('fielddescr', STRUCT, 'mutate_x')
        assert op1.result is None
        assert op2.opname == 'getfield_gc_i'
        assert len(op2.args) == 2
        assert op2.args[0] == v_x
        assert op2.args[1] == ('fielddescr', STRUCT, 'inst_x')
        assert op2.result is op.result
示例#9
0
class LLtypeMixin(object):
    type_system = 'lltype'

    def get_class_of_box(self, box):
        return box.getref(rclass.OBJECTPTR).typeptr

    node_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
    node_vtable.name = rclass.alloc_array_name('node')
    node_vtable_adr = llmemory.cast_ptr_to_adr(node_vtable)
    node_vtable2 = lltype.malloc(OBJECT_VTABLE, immortal=True)
    node_vtable2.name = rclass.alloc_array_name('node2')
    node_vtable_adr2 = llmemory.cast_ptr_to_adr(node_vtable2)
    cpu = runner.LLtypeCPU(None)

    NODE = lltype.GcForwardReference()
    NODE.become(lltype.GcStruct('NODE', ('parent', OBJECT),
                                        ('value', lltype.Signed),
                                        ('floatval', lltype.Float),
                                        ('next', lltype.Ptr(NODE))))
    NODE2 = lltype.GcStruct('NODE2', ('parent', NODE),
                                     ('other', lltype.Ptr(NODE)))
    node = lltype.malloc(NODE)
    node.parent.typeptr = node_vtable
    node2 = lltype.malloc(NODE2)
    node2.parent.parent.typeptr = node_vtable2
    nodebox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node))
    myptr = nodebox.value
    myptr2 = lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(NODE))
    nullptr = lltype.nullptr(llmemory.GCREF.TO)
    nodebox2 = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node2))
    nodesize = cpu.sizeof(NODE)
    nodesize2 = cpu.sizeof(NODE2)
    valuedescr = cpu.fielddescrof(NODE, 'value')
    floatdescr = cpu.fielddescrof(NODE, 'floatval')
    nextdescr = cpu.fielddescrof(NODE, 'next')
    otherdescr = cpu.fielddescrof(NODE2, 'other')

    accessor = FieldListAccessor()
    accessor.initialize(None, {'inst_field': IR_QUASIIMMUTABLE})
    QUASI = lltype.GcStruct('QUASIIMMUT', ('inst_field', lltype.Signed),
                            ('mutate_field', rclass.OBJECTPTR),
                            hints={'immutable_fields': accessor})
    quasisize = cpu.sizeof(QUASI)
    quasi = lltype.malloc(QUASI, immortal=True)
    quasi.inst_field = -4247
    quasifielddescr = cpu.fielddescrof(QUASI, 'inst_field')
    quasibox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, quasi))
    quasiimmutdescr = QuasiImmutDescr(cpu, quasibox,
                                      quasifielddescr,
                                      cpu.fielddescrof(QUASI, 'mutate_field'))

    NODEOBJ = lltype.GcStruct('NODEOBJ', ('parent', OBJECT),
                                         ('ref', lltype.Ptr(OBJECT)))
    nodeobj = lltype.malloc(NODEOBJ)
    nodeobjvalue = lltype.cast_opaque_ptr(llmemory.GCREF, nodeobj)
    refdescr = cpu.fielddescrof(NODEOBJ, 'ref')

    INTOBJ_NOIMMUT = lltype.GcStruct('INTOBJ_NOIMMUT', ('parent', OBJECT),
                                                ('intval', lltype.Signed))
    INTOBJ_IMMUT = lltype.GcStruct('INTOBJ_IMMUT', ('parent', OBJECT),
                                            ('intval', lltype.Signed),
                                            hints={'immutable': True})
    intobj_noimmut_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
    intobj_immut_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
    noimmut_intval = cpu.fielddescrof(INTOBJ_NOIMMUT, 'intval')
    immut_intval = cpu.fielddescrof(INTOBJ_IMMUT, 'intval')

    PTROBJ_IMMUT = lltype.GcStruct('PTROBJ_IMMUT', ('parent', OBJECT),
                                            ('ptrval', lltype.Ptr(OBJECT)),
                                            hints={'immutable': True})
    ptrobj_immut_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
    immut_ptrval = cpu.fielddescrof(PTROBJ_IMMUT, 'ptrval')

    arraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Signed))
    floatarraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Float))

    # a GcStruct not inheriting from OBJECT
    S = lltype.GcStruct('TUPLE', ('a', lltype.Signed), ('b', lltype.Ptr(NODE)))
    ssize = cpu.sizeof(S)
    adescr = cpu.fielddescrof(S, 'a')
    bdescr = cpu.fielddescrof(S, 'b')
    sbox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S)))
    arraydescr2 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(S)))

    T = lltype.GcStruct('TUPLE',
                        ('c', lltype.Signed),
                        ('d', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE)))))
    tsize = cpu.sizeof(T)
    cdescr = cpu.fielddescrof(T, 'c')
    ddescr = cpu.fielddescrof(T, 'd')
    arraydescr3 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(NODE)))

    U = lltype.GcStruct('U',
                        ('parent', OBJECT),
                        ('one', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE)))))
    u_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
    u_vtable_adr = llmemory.cast_ptr_to_adr(u_vtable)
    usize = cpu.sizeof(U)
    onedescr = cpu.fielddescrof(U, 'one')

    FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
    plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                                     EffectInfo.MOST_GENERAL)
    nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                                    EffectInfo([], [], [], []))
    writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                                  EffectInfo([], [], [adescr], []))
    writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                                  EffectInfo([], [], [adescr], [arraydescr]))
    readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                                 EffectInfo([adescr], [], [], []))
    mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                 EffectInfo([nextdescr], [], [], [],
                            EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE,
                            can_invalidate=True))
    arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
             EffectInfo([], [arraydescr], [], [arraydescr],
                        EffectInfo.EF_CANNOT_RAISE,
                        oopspecindex=EffectInfo.OS_ARRAYCOPY))


    # array of structs (complex data)
    complexarray = lltype.GcArray(
        lltype.Struct("complex",
            ("real", lltype.Float),
            ("imag", lltype.Float),
        )
    )
    complexarraydescr = cpu.arraydescrof(complexarray)
    complexrealdescr = cpu.interiorfielddescrof(complexarray, "real")
    compleximagdescr = cpu.interiorfielddescrof(complexarray, "imag")

    for _name, _os in [
        ('strconcatdescr',               'OS_STR_CONCAT'),
        ('strslicedescr',                'OS_STR_SLICE'),
        ('strequaldescr',                'OS_STR_EQUAL'),
        ('streq_slice_checknull_descr',  'OS_STREQ_SLICE_CHECKNULL'),
        ('streq_slice_nonnull_descr',    'OS_STREQ_SLICE_NONNULL'),
        ('streq_slice_char_descr',       'OS_STREQ_SLICE_CHAR'),
        ('streq_nonnull_descr',          'OS_STREQ_NONNULL'),
        ('streq_nonnull_char_descr',     'OS_STREQ_NONNULL_CHAR'),
        ('streq_checknull_char_descr',   'OS_STREQ_CHECKNULL_CHAR'),
        ('streq_lengthok_descr',         'OS_STREQ_LENGTHOK'),
        ]:
        _oopspecindex = getattr(EffectInfo, _os)
        locals()[_name] = \
            cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
                           oopspecindex=_oopspecindex))
        #
        _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
        locals()[_name.replace('str', 'unicode')] = \
            cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
                           oopspecindex=_oopspecindex))

    s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
            EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
    #

    class LoopToken(AbstractDescr):
        pass
    asmdescr = LoopToken() # it can be whatever, it's not a descr though

    from pypy.jit.metainterp.virtualref import VirtualRefInfo
    class FakeWarmRunnerDesc:
        pass
    FakeWarmRunnerDesc.cpu = cpu
    vrefinfo = VirtualRefInfo(FakeWarmRunnerDesc)
    virtualtokendescr = vrefinfo.descr_virtual_token
    virtualforceddescr = vrefinfo.descr_forced
    jit_virtual_ref_vtable = vrefinfo.jit_virtual_ref_vtable
    jvr_vtable_adr = llmemory.cast_ptr_to_adr(jit_virtual_ref_vtable)

    register_known_gctype(cpu, node_vtable,  NODE)
    register_known_gctype(cpu, node_vtable2, NODE2)
    register_known_gctype(cpu, u_vtable,     U)
    register_known_gctype(cpu, jit_virtual_ref_vtable,vrefinfo.JIT_VIRTUAL_REF)
    register_known_gctype(cpu, intobj_noimmut_vtable, INTOBJ_NOIMMUT)
    register_known_gctype(cpu, intobj_immut_vtable,   INTOBJ_IMMUT)
    register_known_gctype(cpu, ptrobj_immut_vtable,   PTROBJ_IMMUT)

    namespace = locals()
示例#10
0
class ExplicitVirtualizableTests:

    XY = lltype.GcStruct(
        'XY',
        ('parent', rclass.OBJECT),
        ('vable_token', lltype.Signed),
        ('inst_x', lltype.Signed),
        ('inst_node', lltype.Ptr(LLtypeMixin.NODE)),
        hints = {'virtualizable2_accessor': FieldListAccessor()})
    XY._hints['virtualizable2_accessor'].initialize(
        XY, {'inst_x' : IR_IMMUTABLE, 'inst_node' : IR_IMMUTABLE})

    xy_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True)
    heaptracker.set_testing_vtable_for_gcstruct(XY, xy_vtable, 'XY')

    def _freeze_(self):
        return True

    def setup(self):
        xy = lltype.malloc(self.XY)
        xy.vable_token = 0
        xy.parent.typeptr = self.xy_vtable
        return xy

    def test_preexisting_access(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'],
                                virtualizables = ['xy'])
        def f(n):
            xy = self.setup()
            xy.inst_x = 10
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n)
                myjitdriver.jit_merge_point(xy=xy, n=n)
                promote_virtualizable(xy, 'inst_x')
                x = xy.inst_x
                xy.inst_x = x + 1
                n -= 1
            promote_virtualizable(xy, 'inst_x')                
            return xy.inst_x
        res = self.meta_interp(f, [20])
        assert res == 30
        self.check_simple_loop(setfield_gc=0, getfield_gc=0)

    def test_preexisting_access_2(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'],
                                virtualizables = ['xy'])
        def f(n):
            xy = self.setup()
            xy.inst_x = 100
            while n > -8:
                myjitdriver.can_enter_jit(xy=xy, n=n)
                myjitdriver.jit_merge_point(xy=xy, n=n)
                if n > 0:
                    promote_virtualizable(xy, 'inst_x')
                    x = xy.inst_x
                    xy.inst_x = x + 1
                else:
                    promote_virtualizable(xy, 'inst_x')
                    x = xy.inst_x
                    xy.inst_x = x + 10
                n -= 1
            promote_virtualizable(xy, 'inst_x')                
            return xy.inst_x
        assert f(5) == 185
        res = self.meta_interp(f, [5])
        assert res == 185
        self.check_resops(setfield_gc=0,
                          getfield_gc=2)  # <= at the header of the loop

    def test_two_paths_access(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'],
                                virtualizables = ['xy'])
        def f(n):
            xy = self.setup()
            xy.inst_x = 100
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n)
                myjitdriver.jit_merge_point(xy=xy, n=n)
                promote_virtualizable(xy, 'inst_x')
                x = xy.inst_x
                if n <= 10:
                    x += 1000
                promote_virtualizable(xy, 'inst_x')                    
                xy.inst_x = x + 1
                n -= 1
            promote_virtualizable(xy, 'inst_x')                
            return xy.inst_x
        res = self.meta_interp(f, [18])
        assert res == 10118
        self.check_resops(setfield_gc=0, getfield_gc=2)

    def test_synchronize_in_return(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'],
                                virtualizables = ['xy'])
        def g(xy, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n)
                myjitdriver.jit_merge_point(xy=xy, n=n)
                promote_virtualizable(xy, 'inst_x')
                xy.inst_x += 1
                n -= 1
        def f(n):
            xy = self.setup()
            xy.inst_x = 10000
            m = 10
            while m > 0:
                g(xy, n)
                m -= 1
            return xy.inst_x
        res = self.meta_interp(f, [18])
        assert res == 10180
        self.check_resops(setfield_gc=0, getfield_gc=2)

    def test_virtualizable_and_greens(self):
        myjitdriver = JitDriver(greens = ['m'], reds = ['n', 'xy'],
                                virtualizables = ['xy'])
        def g(n):
            xy = self.setup()
            xy.inst_x = 10
            m = 0
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n, m=m)
                myjitdriver.jit_merge_point(xy=xy, n=n, m=m)
                promote_virtualizable(xy, 'inst_x')
                x = xy.inst_x
                xy.inst_x = x + 1
                m = (m+1) & 3     # the loop gets unrolled 4 times
                n -= 1
            promote_virtualizable(xy, 'inst_x')                
            return xy.inst_x
        def f(n):
            res = 0
            k = 4
            while k > 0:
                res += g(n)
                k -= 1
            return res
        res = self.meta_interp(f, [40])
        assert res == 50 * 4
        self.check_resops(setfield_gc=0, getfield_gc=4)

    def test_double_frame(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy', 'other'],
                                virtualizables = ['xy'])
        def f(n):
            xy = self.setup()
            xy.inst_x = 10
            other = self.setup()
            other.inst_x = 15
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n, other=other)
                myjitdriver.jit_merge_point(xy=xy, n=n, other=other)
                promote_virtualizable(other, 'inst_x')
                value = other.inst_x         # getfield_gc
                other.inst_x = value + 1     # setfield_gc
                promote_virtualizable(xy, 'inst_x')
                xy.inst_x = value + 100      # virtualized away
                n -= 1
            promote_virtualizable(xy, 'inst_x')                
            return xy.inst_x
        res = self.meta_interp(f, [20])
        assert res == 134
        self.check_simple_loop(setfield_gc=1, getfield_gc=0)
        self.check_resops(setfield_gc=2, getfield_gc=3)

    # ------------------------------

    XY2 = lltype.GcStruct(
        'XY2',
        ('parent', rclass.OBJECT),
        ('vable_token', lltype.Signed),
        ('inst_x', lltype.Signed),
        ('inst_l1', lltype.Ptr(lltype.GcArray(lltype.Signed))),
        ('inst_l2', lltype.Ptr(lltype.GcArray(lltype.Signed))),
        hints = {'virtualizable2_accessor': FieldListAccessor()})
    XY2._hints['virtualizable2_accessor'].initialize(
        XY2, {'inst_x' : IR_IMMUTABLE,
              'inst_l1' : IR_IMMUTABLE_ARRAY, 'inst_l2' : IR_IMMUTABLE_ARRAY})

    xy2_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True)
    heaptracker.set_testing_vtable_for_gcstruct(XY2, xy2_vtable, 'XY2')

    def setup2(self):
        xy2 = lltype.malloc(self.XY2)
        xy2.vable_token = 0
        xy2.parent.typeptr = self.xy2_vtable
        return xy2

    def test_access_list_fields(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'],
                                virtualizables = ['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)
        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 100
            xy2.inst_l1 = lltype.malloc(ARRAY, 3)
            xy2.inst_l1[0] = -9999999
            xy2.inst_l1[1] = -9999999
            xy2.inst_l1[2] = 3001
            xy2.inst_l2 = lltype.malloc(ARRAY, 2)
            xy2.inst_l2[0] = 80
            xy2.inst_l2[1] = -9999999
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                promote_virtualizable(xy2, 'inst_l1')                
                promote_virtualizable(xy2, 'inst_l2')
                xy2.inst_l1[2] += xy2.inst_l2[0]
                n -= 1
            promote_virtualizable(xy2, 'inst_l1')                
            return xy2.inst_l1[2]
        res = self.meta_interp(f, [16])
        assert res == 3001 + 16 * 80
        self.check_simple_loop(setarrayitem_gc=0, setfield_gc=0,
                               getarrayitem_gc=0, getfield_gc=0)

    def test_synchronize_arrays_in_return(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'],
                                virtualizables = ['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)
        def g(xy2, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                promote_virtualizable(xy2, 'inst_x')
                promote_virtualizable(xy2, 'inst_l2')
                xy2.inst_l2[0] += xy2.inst_x
                n -= 1
        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 2
            xy2.inst_l1 = lltype.malloc(ARRAY, 2)
            xy2.inst_l1[0] = 1941309
            xy2.inst_l1[1] = 2941309
            xy2.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.inst_l2[0] = 10000
            m = 10
            while m > 0:
                g(xy2, n)
                m -= 1
            return xy2.inst_l2[0]
        assert f(18) == 10360
        res = self.meta_interp(f, [18])
        assert res == 10360
        self.check_simple_loop(setfield_gc=0, getarrayitem_gc=0,
                               getfield_gc=0, setarrayitem_gc=0)

    def test_array_length(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'],
                                virtualizables = ['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)
        def g(xy2, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                promote_virtualizable(xy2, 'inst_l1')
                promote_virtualizable(xy2, 'inst_l2')                
                xy2.inst_l1[1] += len(xy2.inst_l2)
                n -= 1
        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 2
            xy2.inst_l1 = lltype.malloc(ARRAY, 2)
            xy2.inst_l1[0] = 1941309
            xy2.inst_l1[1] = 2941309
            xy2.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.inst_l2[0] = 10000
            g(xy2, n)
            return xy2.inst_l1[1]
        res = self.meta_interp(f, [18])
        assert res == 2941309 + 18
        self.check_simple_loop(setfield_gc=0, getarrayitem_gc=0,
                               arraylen_gc=0, getfield_gc=0)

    def test_residual_function(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'],
                                virtualizables = ['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)
        #
        @dont_look_inside
        def h(xy2):
            # this function is marked for residual calls because
            # it does something with a virtualizable's array that is not
            # just accessing an item
            return xy2.inst_l2
        #
        def g(xy2, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                promote_virtualizable(xy2, 'inst_l1')
                xy2.inst_l1[1] = xy2.inst_l1[1] + len(h(xy2))
                n -= 1
        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 2
            xy2.inst_l1 = lltype.malloc(ARRAY, 2)
            xy2.inst_l1[0] = 1941309
            xy2.inst_l1[1] = 2941309
            xy2.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.inst_l2[0] = 10000
            g(xy2, n)
            return xy2.inst_l1[1]
        res = self.meta_interp(f, [18])
        assert res == 2941309 + 18
        self.check_simple_loop(call=1, setfield_gc=0, getarrayitem_gc=0,
                               arraylen_gc=1, getfield_gc=0)

    def test_double_frame_array(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2', 'other'],
                                virtualizables = ['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)
        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 10
            xy2.inst_l1 = lltype.malloc(ARRAY, 1)
            xy2.inst_l1[0] = 1982731
            xy2.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.inst_l2[0] = 10000
            other = self.setup2()
            other.inst_x = 15
            other.inst_l1 = lltype.malloc(ARRAY, 2)
            other.inst_l1[0] = 189182
            other.inst_l1[1] = 58421
            other.inst_l2 = lltype.malloc(ARRAY, 2)
            other.inst_l2[0] = 181
            other.inst_l2[1] = 189
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n, other=other)
                myjitdriver.jit_merge_point(xy2=xy2, n=n, other=other)
                promote_virtualizable(other, 'inst_l2')
                length = len(other.inst_l2)       # getfield_gc/arraylen_gc
                value = other.inst_l2[0]          # getfield_gc/getarrayitem_gc
                other.inst_l2[0] = value + length # getfield_gc/setarrayitem_gc
                promote_virtualizable(xy2, 'inst_l2')
                xy2.inst_l2[0] = value + 100      # virtualized away
                n -= 1
            promote_virtualizable(xy2, 'inst_l2')                
            return xy2.inst_l2[0]
        expected = f(20)
        res = self.meta_interp(f, [20], enable_opts='')
        assert res == expected
        self.check_simple_loop(setarrayitem_gc=1, setfield_gc=0,
                               getarrayitem_gc=1, arraylen_gc=1, getfield_gc=1)

    # ------------------------------

    XY2SUB = lltype.GcStruct(
        'XY2SUB',
        ('parent', XY2))

    xy2sub_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True)
    heaptracker.set_testing_vtable_for_gcstruct(XY2SUB, xy2sub_vtable,
                                                'XY2SUB')

    def setup2sub(self):
        xy2 = lltype.malloc(self.XY2SUB)
        xy2.parent.vable_token = 0
        xy2.parent.parent.typeptr = self.xy2_vtable
        return xy2

    def test_subclass(self):
        myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'],
                                virtualizables = ['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)
        def g(xy2, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                parent = xy2.parent
                promote_virtualizable(parent, 'inst_x')                
                promote_virtualizable(parent, 'inst_l2')                
                parent.inst_l2[0] += parent.inst_x
                n -= 1
        def f(n):
            xy2 = self.setup2sub()
            xy2.parent.inst_x = 2
            xy2.parent.inst_l1 = lltype.malloc(ARRAY, 2)
            xy2.parent.inst_l1[0] = 1941309
            xy2.parent.inst_l1[1] = 2941309
            xy2.parent.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.parent.inst_l2[0] = 10000
            m = 10
            while m > 0:
                g(xy2, n)
                m -= 1
            return xy2.parent.inst_l2[0]
        assert f(18) == 10360
        res = self.meta_interp(f, [18])
        assert res == 10360
        self.check_simple_loop(getfield_gc=0, getarrayitem_gc=0,
                               setfield_gc=0, setarrayitem_gc=0)
示例#11
0
class ExplicitVirtualizableTests:

    XY = lltype.GcStruct(
        'XY', ('parent', rclass.OBJECT), ('vable_base', llmemory.Address),
        ('vable_rti', VABLERTIPTR), ('inst_x', lltype.Signed),
        ('inst_node', lltype.Ptr(LLtypeMixin.NODE)),
        hints={'virtualizable2_accessor': FieldListAccessor()})
    XY._hints['virtualizable2_accessor'].initialize(XY, {
        'inst_x': "",
        'inst_node': ""
    })

    xy_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True)
    heaptracker.set_testing_vtable_for_gcstruct(XY, xy_vtable, 'XY')

    def _freeze_(self):
        return True

    def setup(self):
        xy = lltype.malloc(self.XY)
        xy.vable_rti = lltype.nullptr(VABLERTIPTR.TO)
        xy.parent.typeptr = self.xy_vtable
        return xy

    def test_preexisting_access(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy'],
                                virtualizables=['xy'])

        def f(n):
            xy = self.setup()
            xy.inst_x = 10
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n)
                myjitdriver.jit_merge_point(xy=xy, n=n)
                promote_virtualizable(xy, 'inst_x')
                x = xy.inst_x
                xy.inst_x = x + 1
                n -= 1
            promote_virtualizable(xy, 'inst_x')
            return xy.inst_x

        res = self.meta_interp(f, [20])
        assert res == 30
        self.check_loops(getfield_gc=0, setfield_gc=0)

    def test_preexisting_access_2(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy'],
                                virtualizables=['xy'])

        def f(n):
            xy = self.setup()
            xy.inst_x = 100
            while n > -8:
                myjitdriver.can_enter_jit(xy=xy, n=n)
                myjitdriver.jit_merge_point(xy=xy, n=n)
                if n > 0:
                    promote_virtualizable(xy, 'inst_x')
                    x = xy.inst_x
                    xy.inst_x = x + 1
                else:
                    promote_virtualizable(xy, 'inst_x')
                    x = xy.inst_x
                    xy.inst_x = x + 10
                n -= 1
            promote_virtualizable(xy, 'inst_x')
            return xy.inst_x

        assert f(5) == 185
        res = self.meta_interp(f, [5])
        assert res == 185
        self.check_loops(getfield_gc=0, setfield_gc=0)

    def test_two_paths_access(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy'],
                                virtualizables=['xy'])

        def f(n):
            xy = self.setup()
            xy.inst_x = 100
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n)
                myjitdriver.jit_merge_point(xy=xy, n=n)
                promote_virtualizable(xy, 'inst_x')
                x = xy.inst_x
                if n <= 10:
                    x += 1000
                promote_virtualizable(xy, 'inst_x')
                xy.inst_x = x + 1
                n -= 1
            promote_virtualizable(xy, 'inst_x')
            return xy.inst_x

        res = self.meta_interp(f, [18])
        assert res == 10118
        self.check_loops(getfield_gc=0, setfield_gc=0)

    def test_synchronize_in_return(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy'],
                                virtualizables=['xy'])

        def g(xy, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n)
                myjitdriver.jit_merge_point(xy=xy, n=n)
                promote_virtualizable(xy, 'inst_x')
                xy.inst_x += 1
                n -= 1

        def f(n):
            xy = self.setup()
            xy.inst_x = 10000
            m = 10
            while m > 0:
                g(xy, n)
                m -= 1
            return xy.inst_x

        res = self.meta_interp(f, [18])
        assert res == 10180
        self.check_loops(getfield_gc=0, setfield_gc=0)

    def test_virtualizable_and_greens(self):
        myjitdriver = JitDriver(greens=['m'],
                                reds=['n', 'xy'],
                                virtualizables=['xy'])

        def g(n):
            xy = self.setup()
            xy.inst_x = 10
            m = 0
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n, m=m)
                myjitdriver.jit_merge_point(xy=xy, n=n, m=m)
                promote_virtualizable(xy, 'inst_x')
                x = xy.inst_x
                xy.inst_x = x + 1
                m = (m + 1) & 3  # the loop gets unrolled 4 times
                n -= 1
            promote_virtualizable(xy, 'inst_x')
            return xy.inst_x

        def f(n):
            res = 0
            k = 4
            while k > 0:
                res += g(n)
                k -= 1
            return res

        res = self.meta_interp(f, [40])
        assert res == 50 * 4
        self.check_loops(getfield_gc=0, setfield_gc=0)

    def test_double_frame(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy', 'other'],
                                virtualizables=['xy'])

        def f(n):
            xy = self.setup()
            xy.inst_x = 10
            other = self.setup()
            other.inst_x = 15
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n, other=other)
                myjitdriver.jit_merge_point(xy=xy, n=n, other=other)
                promote_virtualizable(other, 'inst_x')
                value = other.inst_x  # getfield_gc
                other.inst_x = value + 1  # setfield_gc
                promote_virtualizable(xy, 'inst_x')
                xy.inst_x = value + 100  # virtualized away
                n -= 1
            promote_virtualizable(xy, 'inst_x')
            return xy.inst_x

        res = self.meta_interp(f, [20])
        assert res == 134
        self.check_loops(getfield_gc=1, setfield_gc=1)

    def test_external_read_while_tracing(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'm', 'xy'],
                                virtualizables=['xy'])

        class Outer:
            pass

        outer = Outer()

        def ext():
            xy = outer.xy
            promote_virtualizable(xy, 'inst_x')
            return xy.inst_x + 2

        def f(n):
            xy = self.setup()
            xy.inst_x = 10
            outer.xy = xy
            m = 0
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n, m=m)
                myjitdriver.jit_merge_point(xy=xy, n=n, m=m)
                promote_virtualizable(xy, 'inst_x')
                xy.inst_x = n + 9998  # virtualized away
                m += ext()  # 2x setfield_gc, 2x getfield_gc
                promote_virtualizable(xy, 'inst_x')
                xy.inst_x = 10  # virtualized away
                n -= 1
            return m

        assert f(20) == 10000 * 20 + (20 * 21) / 2
        res = self.meta_interp(f, [20], policy=StopAtXPolicy(ext))
        assert res == 10000 * 20 + (20 * 21) / 2
        self.check_loops(call=1, getfield_gc=2, setfield_gc=2)
        # xxx for now a call that forces the virtualizable during tracing
        # is supposed to always force it later too.

    def test_external_write_while_tracing(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'm', 'xy'],
                                virtualizables=['xy'])

        class Outer:
            pass

        outer = Outer()

        def ext():
            xy = outer.xy
            promote_virtualizable(xy, 'inst_x')
            xy.inst_x += 2

        def f(n):
            xy = self.setup()
            xy.inst_x = 10
            outer.xy = xy
            m = 0
            while n > 0:
                myjitdriver.can_enter_jit(xy=xy, n=n, m=m)
                myjitdriver.jit_merge_point(xy=xy, n=n, m=m)
                promote_virtualizable(xy, 'inst_x')
                xy.inst_x = n + 9998  # virtualized away
                ext()  # 2x setfield_gc, 2x getfield_gc
                promote_virtualizable(xy, 'inst_x')
                m += xy.inst_x  # virtualized away
                n -= 1
            return m

        res = self.meta_interp(f, [20], policy=StopAtXPolicy(ext))
        assert res == f(20)
        self.check_loops(call=1, getfield_gc=2, setfield_gc=2)
        # xxx for now a call that forces the virtualizable during tracing
        # is supposed to always force it later too.

    # ------------------------------

    XY2 = lltype.GcStruct(
        'XY2', ('parent', rclass.OBJECT), ('vable_base', llmemory.Address),
        ('vable_rti', VABLERTIPTR), ('inst_x', lltype.Signed),
        ('inst_l1', lltype.Ptr(lltype.GcArray(lltype.Signed))),
        ('inst_l2', lltype.Ptr(lltype.GcArray(lltype.Signed))),
        hints={'virtualizable2_accessor': FieldListAccessor()})
    XY2._hints['virtualizable2_accessor'].initialize(XY2, {
        'inst_x': "",
        'inst_l1': "[*]",
        'inst_l2': "[*]"
    })

    xy2_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True)
    heaptracker.set_testing_vtable_for_gcstruct(XY2, xy2_vtable, 'XY2')

    def setup2(self):
        xy2 = lltype.malloc(self.XY2)
        xy2.vable_rti = lltype.nullptr(VABLERTIPTR.TO)
        xy2.parent.typeptr = self.xy2_vtable
        return xy2

    def test_access_list_fields(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy2'],
                                virtualizables=['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)

        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 100
            xy2.inst_l1 = lltype.malloc(ARRAY, 3)
            xy2.inst_l1[0] = -9999999
            xy2.inst_l1[1] = -9999999
            xy2.inst_l1[2] = 3001
            xy2.inst_l2 = lltype.malloc(ARRAY, 2)
            xy2.inst_l2[0] = 80
            xy2.inst_l2[1] = -9999999
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                promote_virtualizable(xy2, 'inst_l1')
                promote_virtualizable(xy2, 'inst_l2')
                xy2.inst_l1[2] += xy2.inst_l2[0]
                n -= 1
            promote_virtualizable(xy2, 'inst_l1')
            return xy2.inst_l1[2]

        res = self.meta_interp(f, [16])
        assert res == 3001 + 16 * 80
        self.check_loops(getfield_gc=0,
                         setfield_gc=0,
                         getarrayitem_gc=0,
                         setarrayitem_gc=0)

    def test_synchronize_arrays_in_return(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy2'],
                                virtualizables=['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)

        def g(xy2, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                promote_virtualizable(xy2, 'inst_x')
                promote_virtualizable(xy2, 'inst_l2')
                xy2.inst_l2[0] += xy2.inst_x
                n -= 1

        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 2
            xy2.inst_l1 = lltype.malloc(ARRAY, 2)
            xy2.inst_l1[0] = 1941309
            xy2.inst_l1[1] = 2941309
            xy2.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.inst_l2[0] = 10000
            m = 10
            while m > 0:
                g(xy2, n)
                m -= 1
            return xy2.inst_l2[0]

        assert f(18) == 10360
        res = self.meta_interp(f, [18])
        assert res == 10360
        self.check_loops(getfield_gc=0, setfield_gc=0, getarrayitem_gc=0)

    def test_array_length(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy2'],
                                virtualizables=['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)

        def g(xy2, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                promote_virtualizable(xy2, 'inst_l1')
                promote_virtualizable(xy2, 'inst_l2')
                xy2.inst_l1[1] += len(xy2.inst_l2)
                n -= 1

        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 2
            xy2.inst_l1 = lltype.malloc(ARRAY, 2)
            xy2.inst_l1[0] = 1941309
            xy2.inst_l1[1] = 2941309
            xy2.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.inst_l2[0] = 10000
            g(xy2, n)
            return xy2.inst_l1[1]

        res = self.meta_interp(f, [18])
        assert res == 2941309 + 18
        self.check_loops(getfield_gc=0,
                         setfield_gc=0,
                         getarrayitem_gc=0,
                         arraylen_gc=0)

    def test_residual_function(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy2'],
                                virtualizables=['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)
        #
        @dont_look_inside
        def h(xy2):
            # this function is marked for residual calls because
            # it does something with a virtualizable's array that is not
            # just accessing an item
            return xy2.inst_l2

        #
        def g(xy2, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                promote_virtualizable(xy2, 'inst_l1')
                xy2.inst_l1[1] = xy2.inst_l1[1] + len(h(xy2))
                n -= 1

        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 2
            xy2.inst_l1 = lltype.malloc(ARRAY, 2)
            xy2.inst_l1[0] = 1941309
            xy2.inst_l1[1] = 2941309
            xy2.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.inst_l2[0] = 10000
            g(xy2, n)
            return xy2.inst_l1[1]

        res = self.meta_interp(f, [18])
        assert res == 2941309 + 18
        self.check_loops(getfield_gc=0,
                         setfield_gc=0,
                         getarrayitem_gc=0,
                         arraylen_gc=1,
                         call=1)

    def test_double_frame_array(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy2', 'other'],
                                virtualizables=['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)

        def f(n):
            xy2 = self.setup2()
            xy2.inst_x = 10
            xy2.inst_l1 = lltype.malloc(ARRAY, 1)
            xy2.inst_l1[0] = 1982731
            xy2.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.inst_l2[0] = 10000
            other = self.setup2()
            other.inst_x = 15
            other.inst_l1 = lltype.malloc(ARRAY, 2)
            other.inst_l1[0] = 189182
            other.inst_l1[1] = 58421
            other.inst_l2 = lltype.malloc(ARRAY, 2)
            other.inst_l2[0] = 181
            other.inst_l2[1] = 189
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n, other=other)
                myjitdriver.jit_merge_point(xy2=xy2, n=n, other=other)
                promote_virtualizable(other, 'inst_l2')
                length = len(other.inst_l2)  # getfield_gc/arraylen_gc
                value = other.inst_l2[0]  # getfield_gc/getarrayitem_gc
                other.inst_l2[
                    0] = value + length  # getfield_gc/setarrayitem_gc
                promote_virtualizable(xy2, 'inst_l2')
                xy2.inst_l2[0] = value + 100  # virtualized away
                n -= 1
            promote_virtualizable(xy2, 'inst_l2')
            return xy2.inst_l2[0]

        expected = f(20)
        res = self.meta_interp(f, [20], optimizer=OPTIMIZER_SIMPLE)
        assert res == expected
        self.check_loops(getfield_gc=3,
                         setfield_gc=0,
                         arraylen_gc=1,
                         getarrayitem_gc=1,
                         setarrayitem_gc=1)

    # ------------------------------

    XY2SUB = lltype.GcStruct('XY2SUB', ('parent', XY2))

    xy2sub_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True)
    heaptracker.set_testing_vtable_for_gcstruct(XY2SUB, xy2sub_vtable,
                                                'XY2SUB')

    def setup2sub(self):
        xy2 = lltype.malloc(self.XY2SUB)
        xy2.parent.vable_rti = lltype.nullptr(VABLERTIPTR.TO)
        xy2.parent.parent.typeptr = self.xy2_vtable
        return xy2

    def test_subclass(self):
        myjitdriver = JitDriver(greens=[],
                                reds=['n', 'xy2'],
                                virtualizables=['xy2'])
        ARRAY = lltype.GcArray(lltype.Signed)

        def g(xy2, n):
            while n > 0:
                myjitdriver.can_enter_jit(xy2=xy2, n=n)
                myjitdriver.jit_merge_point(xy2=xy2, n=n)
                parent = xy2.parent
                promote_virtualizable(parent, 'inst_x')
                promote_virtualizable(parent, 'inst_l2')
                parent.inst_l2[0] += parent.inst_x
                n -= 1

        def f(n):
            xy2 = self.setup2sub()
            xy2.parent.inst_x = 2
            xy2.parent.inst_l1 = lltype.malloc(ARRAY, 2)
            xy2.parent.inst_l1[0] = 1941309
            xy2.parent.inst_l1[1] = 2941309
            xy2.parent.inst_l2 = lltype.malloc(ARRAY, 1)
            xy2.parent.inst_l2[0] = 10000
            m = 10
            while m > 0:
                g(xy2, n)
                m -= 1
            return xy2.parent.inst_l2[0]

        assert f(18) == 10360
        res = self.meta_interp(f, [18])
        assert res == 10360
        self.check_loops(getfield_gc=0, setfield_gc=0, getarrayitem_gc=0)