
""" Tests for register allocation for common constructs
"""

import py
from rpython.jit.metainterp.history import (BasicFailDescr,
                                        BasicFinalDescr,
                                        JitCellToken,
                                        TargetToken)
from rpython.jit.metainterp.resoperation import rop
from rpython.jit.backend.llsupport.descr import GcCache
from rpython.jit.backend.detect_cpu import getcpuclass
from rpython.jit.backend.arm.regalloc import Regalloc, ARMFrameManager
from rpython.jit.backend.llsupport.regalloc import is_comparison_or_ovf_op
from rpython.jit.tool.oparser import parse
from rpython.rtyper.lltypesystem import lltype, llmemory
from rpython.rtyper.annlowlevel import llhelper
from rpython.rtyper.lltypesystem import rclass, rstr
from rpython.jit.codewriter.effectinfo import EffectInfo
from rpython.jit.codewriter import longlong
from rpython.jit.backend.llsupport.test.test_regalloc_integration import BaseTestRegalloc


def test_is_comparison_or_ovf_op():
    assert not is_comparison_or_ovf_op(rop.INT_ADD)
    assert is_comparison_or_ovf_op(rop.INT_ADD_OVF)
    assert is_comparison_or_ovf_op(rop.INT_EQ)

CPU = getcpuclass()


class MockGcDescr(GcCache):
    def get_funcptr_for_new(self):
        return 123
    get_funcptr_for_newarray = get_funcptr_for_new
    get_funcptr_for_newstr = get_funcptr_for_new
    get_funcptr_for_newunicode = get_funcptr_for_new

    def rewrite_assembler(self, cpu, operations):
        pass


class MockAssembler(object):
    gcrefs = None
    _float_constants = None

    def __init__(self, cpu=None, gc_ll_descr=None):
        self.movs = []
        self.performs = []
        self.lea = []
        if cpu is None:
            cpu = CPU(None, None)
            cpu.setup_once()
        self.cpu = cpu
        if gc_ll_descr is None:
            gc_ll_descr = MockGcDescr(False)
        self.cpu.gc_ll_descr = gc_ll_descr

    def dump(self, *args):
        pass

    def regalloc_mov(self, from_loc, to_loc):
        self.movs.append((from_loc, to_loc))

    def regalloc_perform(self, op, arglocs, resloc):
        self.performs.append((op, arglocs, resloc))

    def regalloc_perform_discard(self, op, arglocs):
        self.performs.append((op, arglocs))

    def load_effective_addr(self, *args):
        self.lea.append(args)


class RegAllocForTests(Regalloc):
    position = 0

    def _compute_next_usage(self, v, _):
        return -1


def get_zero_division_error(self):
    # for tests, a random emulated ll_inst will do
    ll_inst = lltype.malloc(rclass.OBJECT)
    ll_inst.typeptr = lltype.malloc(rclass.OBJECT_VTABLE,
                                    immortal=True)
    _zer_error_vtable = llmemory.cast_ptr_to_adr(ll_inst.typeptr)
    zer_vtable = self.cast_adr_to_int(_zer_error_vtable)
    zer_inst = lltype.cast_opaque_ptr(llmemory.GCREF, ll_inst)
    return zer_vtable, zer_inst


class CustomBaseTestRegalloc(BaseTestRegalloc):
    cpu = CPU(None, None)
    cpu.setup_once()

    def raising_func(i):
        if i:
            raise LLException(zero_division_error,
                              zero_division_value)
    FPTR = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Void))
    raising_fptr = llhelper(FPTR, raising_func)

    def f(a):
        return 23

    FPTR = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Signed))
    f_fptr = llhelper(FPTR, f)
    f_calldescr = cpu.calldescrof(FPTR.TO, FPTR.TO.ARGS, FPTR.TO.RESULT,
                                                    EffectInfo.MOST_GENERAL)

    zero_division_tp, zero_division_value = get_zero_division_error(cpu)
    zd_addr = cpu.cast_int_to_adr(zero_division_tp)
    zero_division_error = llmemory.cast_adr_to_ptr(zd_addr,
                                            lltype.Ptr(rclass.OBJECT_VTABLE))
    raising_calldescr = cpu.calldescrof(FPTR.TO, FPTR.TO.ARGS, FPTR.TO.RESULT,
                                                    EffectInfo.MOST_GENERAL)

    targettoken = TargetToken()
    targettoken2 = TargetToken()
    fdescr1 = BasicFailDescr(1)
    fdescr2 = BasicFailDescr(2)
    fdescr3 = BasicFailDescr(3)

    def setup_method(self, meth):
        self.targettoken._ll_loop_code = 0
        self.targettoken2._ll_loop_code = 0

    def f1(x):
        return x + 1

    def f2(x, y):
        return x * y

    def f10(*args):
        assert len(args) == 10
        return sum(args)

    F1PTR = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Signed))
    F2PTR = lltype.Ptr(lltype.FuncType([lltype.Signed] * 2, lltype.Signed))
    F10PTR = lltype.Ptr(lltype.FuncType([lltype.Signed] * 10, lltype.Signed))
    f1ptr = llhelper(F1PTR, f1)
    f2ptr = llhelper(F2PTR, f2)
    f10ptr = llhelper(F10PTR, f10)

    f1_calldescr = cpu.calldescrof(F1PTR.TO, F1PTR.TO.ARGS, F1PTR.TO.RESULT,
                                                    EffectInfo.MOST_GENERAL)
    f2_calldescr = cpu.calldescrof(F2PTR.TO, F2PTR.TO.ARGS, F2PTR.TO.RESULT,
                                                    EffectInfo.MOST_GENERAL)
    f10_calldescr = cpu.calldescrof(F10PTR.TO, F10PTR.TO.ARGS,
                                    F10PTR.TO.RESULT, EffectInfo.MOST_GENERAL)
    typesystem = 'lltype'
    namespace = locals().copy()

class TestRegallocSimple(CustomBaseTestRegalloc):
    def test_simple_loop(self):
        ops = '''
        [i0]
        label(i0, descr=targettoken)
        i1 = int_add(i0, 1)
        i2 = int_lt(i1, 20)
        guard_true(i2) [i1]
        jump(i1, descr=targettoken)
        '''
        self.interpret(ops, [0])
        assert self.getint(0) == 20

    def test_two_loops_and_a_bridge(self):
        ops = '''
        [i0, i1, i2, i3]
        label(i0, i1, i2, i3, descr=targettoken)
        i4 = int_add(i0, 1)
        i5 = int_lt(i4, 20)
        guard_true(i5) [i4, i1, i2, i3]
        jump(i4, i1, i2, i3, descr=targettoken)
        '''
        loop = self.interpret(ops, [0, 0, 0, 0])
        ops2 = '''
        [i5, i6, i7, i8]
        label(i5, descr=targettoken2)
        i1 = int_add(i5, 1)
        i3 = int_add(i1, 1)
        i4 = int_add(i3, 1)
        i2 = int_lt(i4, 30)
        guard_true(i2) [i4]
        jump(i4, descr=targettoken2)
        '''
        loop2 = self.interpret(ops2, [0, 0, 0, 0])
        bridge_ops = '''
        [i4]
        jump(i4, i4, i4, i4, descr=targettoken)
        '''
        bridge = self.attach_bridge(bridge_ops, loop2, 5)
        self.run(loop2, 0, 0, 0, 0)
        assert self.getint(0) == 31
        assert self.getint(1) == 30
        assert self.getint(2) == 30
        assert self.getint(3) == 30

    def test_pointer_arg(self):
        ops = '''
        [i0, p0]
        label(i0, p0, descr=targettoken)
        i1 = int_add(i0, 1)
        i2 = int_lt(i1, 10)
        guard_true(i2) [p0]
        jump(i1, p0, descr=targettoken)
        '''
        S = lltype.GcStruct('S')
        ptr = lltype.malloc(S)
        self.interpret(ops, [0, ptr])
        assert self.getptr(0, lltype.Ptr(S)) == ptr

    def test_exception_bridge_no_exception(self):
        ops = '''
        [i0]
        i1 = same_as(1)
        call(ConstClass(raising_fptr), i0, descr=raising_calldescr)
        guard_exception(ConstClass(zero_division_error)) [i1]
        finish(0)
        '''
        bridge_ops = '''
        [i3]
        i2 = same_as(2)
        guard_no_exception() [i2]
        finish(1)
        '''
        loop = self.interpret(ops, [0])
        assert self.getint(0) == 1
        bridge = self.attach_bridge(bridge_ops, loop, 2)
        self.run(loop, 0)
        assert self.getint(0) == 1

    def test_inputarg_unused(self):
        ops = '''
        [i0]
        finish(1)
        '''
        self.interpret(ops, [0])
        # assert did not explode

    def test_nested_guards(self):
        ops = '''
        [i0, i1]
        guard_true(i0) [i0, i1]
        finish(4)
        '''
        bridge_ops = '''
        [i0, i1]
        guard_true(i0) [i0, i1]
        finish(3)
        '''
        loop = self.interpret(ops, [0, 10])
        assert self.getint(0) == 0
        assert self.getint(1) == 10
        bridge = self.attach_bridge(bridge_ops, loop, 0)
        self.run(loop, 0, 10)
        assert self.getint(0) == 0
        assert self.getint(1) == 10

    def test_nested_unused_arg(self):
        ops = '''
        [i0, i1]
        guard_true(i0) [i0, i1]
        finish(1)
        '''
        loop = self.interpret(ops, [0, 1])
        assert self.getint(0) == 0
        bridge_ops = '''
        [i0, i1]
        guard_true(i0) [i0]
        finish(1)
        '''
        self.attach_bridge(bridge_ops, loop, 0)
        self.run(loop, 0, 1)

    def test_spill_for_constant(self):
        ops = '''
        [i0, i1, i2, i3]
        label(i0, i1, i2, i3, descr=targettoken)
        i4 = int_add(3, i1)
        i5 = int_lt(i4, 30)
        guard_true(i5) [i0, i4, i2, i3]
        jump(1, i4, 3, 4, descr=targettoken)
        '''
        self.interpret(ops, [0, 0, 0, 0])
        assert self.getints(4) == [1, 30, 3, 4]

    def test_spill_for_constant_lshift(self):
        ops = '''
        [i0, i2, i1, i3]
        label(i0, i2, i1, i3, descr=targettoken)
        i4 = int_lshift(1, i1)
        i5 = int_add(1, i1)
        i6 = int_lt(i5, 30)
        guard_true(i6) [i4, i5, i2, i3]
        jump(i4, 3, i5, 4, descr=targettoken)
        '''
        self.interpret(ops, [0, 0, 0, 0])
        assert self.getints(4) == [1<<29, 30, 3, 4]
        ops = '''
        [i0, i1, i2, i3]
        label(i0, i1, i2, i3, descr=targettoken)
        i4 = int_lshift(1, i1)
        i5 = int_add(1, i1)
        i6 = int_lt(i5, 30)
        guard_true(i6) [i4, i5, i2, i3]
        jump(i4, i5, 3, 4, descr=targettoken)
        '''
        self.interpret(ops, [0, 0, 0, 0])
        assert self.getints(4) == [1<<29, 30, 3, 4]
        ops = '''
        [i0, i3, i1, i2]
        label(i0, i3, i1, i2, descr=targettoken)
        i4 = int_lshift(1, i1)
        i5 = int_add(1, i1)
        i6 = int_lt(i5, 30)
        guard_true(i6) [i4, i5, i2, i3]
        jump(i4, 4, i5, 3, descr=targettoken)
        '''
        self.interpret(ops, [0, 0, 0, 0])
        assert self.getints(4) == [1<<29, 30, 3, 4]

    def test_result_selected_reg_via_neg(self):
        ops = '''
        [i0, i1, i2, i3]
        label(i0, i1, i2, i3, descr=targettoken)
        i6 = int_neg(i2)
        i7 = int_add(1, i1)
        i4 = int_lt(i7, 10)
        guard_true(i4) [i0, i6, i7]
        jump(1, i7, i2, i6, descr=targettoken)
        '''
        self.interpret(ops, [0, 0, 3, 0])
        assert self.getints(3) == [1, -3, 10]
        
    def test_compare_memory_result_survives(self):
        ops = '''
        [i0, i1, i2, i3]
        label(i0, i1, i2, i3, descr=targettoken)
        i4 = int_lt(i0, i1)
        i5 = int_add(i3, 1)
        i6 = int_lt(i5, 30)
        guard_true(i6) [i4]
        jump(i0, i1, i4, i5, descr=targettoken)
        '''
        self.interpret(ops, [0, 10, 0, 0])
        assert self.getint(0) == 1

    def test_jump_different_args(self):
        ops = '''
        [i0, i15, i16, i18, i1, i2, i3]
        label(i0, i15, i16, i18, i1, i2, i3, descr=targettoken)
        i4 = int_add(i3, 1)
        i5 = int_lt(i4, 20)
        guard_true(i5) [i2, i1]
        jump(i0, i18, i15, i16, i2, i1, i4, descr=targettoken)
        '''
        self.interpret(ops, [0, 1, 2, 3, 0, 0, 0])

    def test_op_result_unused(self):
        ops = '''
        [i0, i1]
        i2 = int_add(i0, i1)
        finish(0)
        '''
        self.interpret(ops, [0, 0])

    def test_guard_value_two_boxes(self):
        ops = '''
        [i0, i1, i2, i3, i4, i5, i6, i7]
        guard_value(i6, i1) [i0, i2, i3, i4, i5, i6]
        finish(i0)
        '''
        self.interpret(ops, [0, 0, 0, 0, 0, 0, 0, 0])
        assert self.getint(0) == 0

    def test_bug_wrong_stack_adj(self):
        ops = '''
        [i0, i1, i2, i3, i4, i5, i6, i7, i8]
        i9 = same_as(0)
        guard_true(i0) [i9, i0, i1, i2, i3, i4, i5, i6, i7, i8]
        finish(1)
        '''
        loop = self.interpret(ops, [0, 1, 2, 3, 4, 5, 6, 7, 8])
        assert self.getint(0) == 0
        bridge_ops = '''
        [i9, i0, i1, i2, i3, i4, i5, i6, i7, i8]
        call(ConstClass(raising_fptr), 0, descr=raising_calldescr)
        guard_true(i0) [i0, i1, i2, i3, i4, i5, i6, i7, i8]
        finish(1)
        '''
        self.attach_bridge(bridge_ops, loop, 1)
        self.run(loop, 0, 1, 2, 3, 4, 5, 6, 7, 8)
        assert self.getints(9) == range(9)

    def test_loopargs(self):
        ops = """
        [i0, i1, i2, i3]
        i4 = int_add(i0, i1)
        jump(i4, i1, i2, i3)
        """
        regalloc = self.prepare_loop(ops)
        assert len(regalloc.rm.reg_bindings) == 0
        assert len(regalloc.frame_manager.bindings) == 4

    def test_loopargs_2(self):
        ops = """
        [i0, i1, i2, i3]
        i4 = int_add(i0, i1)
        guard_false(i0) [i4, i1, i2, i3]
        """
        regalloc = self.prepare_loop(ops)
        assert len(regalloc.frame_manager.bindings) == 4

    def test_loopargs_3(self):
        ops = """
        [i0, i1, i2, i3]
        i4 = int_add(i0, i1)
        guard_true(i4) [i0, i1, i2, i3, i4]
        jump(i4, i1, i2, i3)
        """
        regalloc = self.prepare_loop(ops)
        assert len(regalloc.frame_manager.bindings) == 4


class TestRegallocCompOps(CustomBaseTestRegalloc):

    def test_cmp_op_0(self):
        ops = '''
        [i0, i3]
        i1 = same_as(1)
        i2 = int_lt(i0, 100)
        guard_true(i3) [i1, i2]
        finish(i2)
        '''
        self.interpret(ops, [0, 1])
        assert self.getint(0) == 1


class TestRegallocMoreRegisters(CustomBaseTestRegalloc):

    cpu = CustomBaseTestRegalloc.cpu
    targettoken = TargetToken()

    S = lltype.GcStruct('S', ('field', lltype.Char))
    fielddescr = cpu.fielddescrof(S, 'field')

    A = lltype.GcArray(lltype.Char)
    I = lltype.GcArray(lltype.Signed)
    arraydescr = cpu.arraydescrof(A)
    arraydescr_i = cpu.arraydescrof(I)

    namespace = locals().copy()

    def test_int_is_true(self):
        ops = '''
        [i0, i1, i2, i3, i4, i5, i6, i7]
        i10 = int_is_true(i0)
        i11 = int_is_true(i1)
        i12 = int_is_true(i2)
        i13 = int_is_true(i3)
        i14 = int_is_true(i4)
        i15 = int_is_true(i5)
        i16 = int_is_true(i6)
        i17 = int_is_true(i7)
        guard_true(i0) [i10, i11, i12, i13, i14, i15, i16, i17]
        '''
        self.interpret(ops, [0, 42, 12, 0, 13, 0, 0, 3333])
        assert self.getints(8) == [0, 1, 1, 0, 1, 0, 0, 1]

    def test_comparison_ops(self):
        ops = '''
        [i0, i1, i2, i3, i4, i5, i6]
        i10 = int_lt(i0, i1)
        i11 = int_le(i2, i3)
        i12 = int_ge(i4, i5)
        i13 = int_eq(i5, i6)
        i14 = int_gt(i6, i2)
        i15 = int_ne(i2, i6)
        guard_true(i15) [i10, i11, i12, i13, i14, i15]

        '''
        self.interpret(ops, [0, 1, 2, 3, 4, 5, 6])
        assert self.getints(6) == [1, 1, 0, 0, 1, 1]

    def test_strsetitem(self):
        ops = '''
        [p0, i]
        strsetitem(p0, 1, i)
        finish()
        '''
        llstr = rstr.mallocstr(10)
        self.interpret(ops, [llstr, ord('a')])
        assert llstr.chars[1] == 'a'

    def test_setfield_char(self):
        ops = '''
        [p0, i]
        setfield_gc(p0, i, descr=fielddescr)
        finish()
        '''
        s = lltype.malloc(self.S)
        self.interpret(ops, [s, ord('a')])
        assert s.field == 'a'

    def test_setarrayitem_gc(self):
        ops = '''
        [p0, i]
        setarrayitem_gc(p0, 1, i, descr=arraydescr)
        finish()
        '''
        s = lltype.malloc(self.A, 3)
        self.interpret(ops, [s, ord('a')])
        assert s[1] == 'a'

    def test_setarrayitem2_gc(self):
        ops = '''
        [p0, i, i1]
        setarrayitem_gc(p0, i1, i, descr=arraydescr)
        finish()
        '''
        s = lltype.malloc(self.A, 3)
        self.interpret(ops, [s, ord('a'), 1])
        assert s[1] == 'a'

    def test_setarrayitem3_gc(self):
        ops = '''
        [p0, i0, i1]
        setarrayitem_gc(p0, i1, i0, descr=arraydescr_i)
        finish()
        '''
        s = lltype.malloc(self.I, 3)
        self.interpret(ops, [s, 1234567890, 1])
        assert s[1] == 1234567890

    def test_setarrayitem4_gc(self):
        ops = '''
        [p0, i0]
        setarrayitem_gc(p0, 1, i0, descr=arraydescr_i)
        finish()
        '''
        s = lltype.malloc(self.I, 3)
        self.interpret(ops, [s, 1234567890])
        assert s[1] == 1234567890

    def test_division_optimized(self):
        ops = '''
        [i7, i6]
        label(i7, i6, descr=targettoken)
        i18 = int_floordiv(i7, i6)
        i19 = int_xor(i7, i6)
        i21 = int_lt(i19, 0)
        i22 = int_mod(i7, i6)
        i23 = int_is_true(i22)
        i24 = int_eq(i6, 4)
        guard_false(i24) [i18]
        jump(i18, i6, descr=targettoken)
        '''
        self.interpret(ops, [10, 4])
        assert self.getint(0) == 2
        # FIXME: Verify that i19 - i23 are removed


class TestRegallocFloats(CustomBaseTestRegalloc):
    def test_float_add(self):
        if not self.cpu.supports_floats:
            py.test.skip("requires floats")
        ops = '''
        [f0, f1]
        f2 = float_add(f0, f1)
        guard_value(f0, f1) [f2, f0, f1]
        '''
        self.interpret(ops, [3.0, 1.5])
        assert self.getfloats(3) == [4.5, 3.0, 1.5]

    def test_float_adds_stack(self):
        if not self.cpu.supports_floats:
            py.test.skip("requires floats")
        ops = '''
        [f0, f1, f2, f3, f4, f5, f6, f7, f8]
        f9 = float_add(f0, f1)
        f10 = float_add(f8, 3.5)
        guard_value(f0, f1) [f9, f10, f2, f3, f4, f5, f6, f7, f8]
        '''
        self.interpret(ops, [0.1, .2, .3, .4, .5, .6, .7, .8, .9])
        assert self.getfloats(9) == [.1 + .2, .9 + 3.5, .3,
                                        .4, .5, .6, .7, .8, .9]

    def test_lt_const(self):
        if not self.cpu.supports_floats:
            py.test.skip("requires floats")
        ops = '''
        [f0]
        i1 = float_lt(3.5, f0)
        finish(i1)
        '''
        self.interpret(ops, [0.1])
        assert self.getint(0) == 0

    def test_bug_float_is_true_stack(self):
        if not self.cpu.supports_floats:
            py.test.skip("requires floats")
        # NB. float_is_true no longer exists.  Unsure if keeping this test
        # makes sense any more.
        ops = '''
        [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9]
        i0 = float_ne(f0, 0.0)
        i1 = float_ne(f1, 0.0)
        i2 = float_ne(f2, 0.0)
        i3 = float_ne(f3, 0.0)
        i4 = float_ne(f4, 0.0)
        i5 = float_ne(f5, 0.0)
        i6 = float_ne(f6, 0.0)
        i7 = float_ne(f7, 0.0)
        i8 = float_ne(f8, 0.0)
        i9 = float_ne(f9, 0.0)
        guard_true(i9), [i0, i1, i2, i3, i4, i5, i6, i7, i8, i9]
        '''
        self.interpret(ops, [0.0, .1, .2, .3, .4, .5, .6, .7, .8, .9])
        assert self.getints(9) == [0, 1, 1, 1, 1, 1, 1, 1, 1]


class TestRegAllocCallAndStackDepth(CustomBaseTestRegalloc):
    def expected_param_depth(self, num_args):
        # Assumes the arguments are all non-float
        return num_args

    def test_one_call(self):
        ops = '''
        [i0, i1, i2, i3, i4, i5, i6, i7, i8, i9]
        i10 = call(ConstClass(f1ptr), i0, descr=f1_calldescr)
        guard_true(i10), [i10, i1, i2, i3, i4, i5, i6, i7, i8, i9]
        '''
        self.interpret(ops, [4, 7, 9, 9, 9, 9, 9, 9, 9, 9])
        assert self.getints(10) == [5, 7, 9, 9, 9, 9, 9, 9, 9, 9]

    def test_two_calls(self):
        ops = '''
        [i0, i1,  i2, i3, i4, i5, i6, i7, i8, i9]
        i10 = call(ConstClass(f1ptr), i0, descr=f1_calldescr)
        i11 = call(ConstClass(f2ptr), i10, i1, descr=f2_calldescr)
        guard_true(i11) [i11, i1,  i2, i3, i4, i5, i6, i7, i8, i9]
        '''
        self.interpret(ops, [4, 7, 9, 9, 9, 9, 9, 9, 9, 9])
        assert self.getints(10) == [5 * 7, 7, 9, 9, 9, 9, 9, 9, 9, 9]

    def test_call_many_arguments(self):
        ops = '''
        [i0, i1, i2, i3, i4, i5, i6, i7]
        i8 = call(ConstClass(f10ptr), 1, i0, i1, i2, i3, i4, i5, i6, i7, 10, descr=f10_calldescr)
        finish(i8)
        '''
        self.interpret(ops, [2, 3, 4, 5, 6, 7, 8, 9])
        assert self.getint(0) == 55

    def test_bridge_calls_1(self):
        ops = '''
        [i0, i1]
        i2 = call(ConstClass(f1ptr), i0, descr=f1_calldescr)
        guard_value(i2, 0, descr=fdescr1) [i2, i1]
        finish(i1)
        '''
        loop = self.interpret(ops, [4, 7])
        assert self.getint(0) == 5
        ops = '''
        [i2, i1]
        i3 = call(ConstClass(f2ptr), i2, i1, descr=f2_calldescr)
        finish(i3)
        '''
        self.attach_bridge(ops, loop, -2)

        self.run(loop, 4, 7)
        assert self.getint(0) == 5 * 7

    def test_bridge_calls_2(self):
        ops = '''
        [i0, i1]
        i2 = call(ConstClass(f2ptr), i0, i1, descr=f2_calldescr)
        guard_value(i2, 0, descr=fdescr1) [i2]
        finish(i1)
        '''
        loop = self.interpret(ops, [4, 7])
        assert self.getint(0) == 4 * 7
        ops = '''
        [i2]
        i3 = call(ConstClass(f1ptr), i2, descr=f1_calldescr)
        finish(i3)
        '''
        self.attach_bridge(ops, loop, -2)

        self.run(loop, 4, 7)
        assert self.getint(0) == 29


class TestJumps(TestRegallocSimple):
    def test_jump_with_consts(self):
        loop = """
        [i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14]
        label(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, descr=targettoken)
        jump(i1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, descr=targettoken)
        """
        self.interpret(loop, range(15), run=False)
        # ensure compiling this loop works
        assert 1

    def test_from_loop_to_loop(self):
        def assembler_helper(failindex, virtualizable):
            return 3

        FUNCPTR = lltype.Ptr(lltype.FuncType([lltype.Signed, llmemory.GCREF],
                                             lltype.Signed))

        class FakeJitDriverSD:
            index_of_virtualizable = -1
            _assembler_helper_ptr = llhelper(FUNCPTR, assembler_helper)
            assembler_helper_adr = llmemory.cast_ptr_to_adr(
                _assembler_helper_ptr)

        FakeJitDriverSD.portal_calldescr = self.cpu.calldescrof(
            lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Signed)), \
                    [lltype.Signed], lltype.Signed, EffectInfo.MOST_GENERAL)
        self.cpu.done_with_this_frame_descr_int = BasicFinalDescr()
        loop1 = """
        [i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10]
        i11 = int_add(i0, i1)
        guard_false(i0) [i11, i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10]
        """
        large = self.interpret(loop1, range(11), run=False)
        large._jitcelltoken.outermost_jitdriver_sd = FakeJitDriverSD()
        self.namespace['looptoken'] = large._jitcelltoken
        assert self.namespace['looptoken']._ll_function_addr != 0
        loop2 = """
        [i0]
        i1 = force_token()
        i2 = call_assembler(1,2,3,4,5,6,7,8,9,10,11, descr=looptoken)
        guard_not_forced() [i0]
        guard_false(i0) [i0, i2]
        """

        self.interpret(loop2, [110])
        assert self.getint(0) == 110
        assert self.getint(1) == 3

    def test_far_far_jump(self):
        ops = """
        [i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10]
        label(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, descr=targettoken)
        i11 = int_add(i0, 1)
        i12 = int_lt(i11, 2)
        i13 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i14 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i15 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i16 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i17 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i18 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i19 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i20 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i21 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i22 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i23 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i24 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i26 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i27 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i28 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i29 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        i30 = call(ConstClass(f_fptr), i12, descr=f_calldescr)
        guard_true(i12) [i11, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10]
        jump(i11, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, descr=targettoken)
        """
        self.interpret(ops, range(11))
        assert self.getint(0) == 2  # and not segfault()


class TestStrOps(CustomBaseTestRegalloc):
    def test_newstr(self):
        ops = """
        [i0]
        p1 = newstr(300)
        i2 = strlen(p1)
        finish(i2)
        """
        self.interpret(ops, [0])
        assert self.getint(0) == 300
        ops = """
        [i0]
        p1 = newstr(i0)
        i2 = strlen(p1)
        finish(i2)
        """
        self.interpret(ops, [300])
        assert self.getint(0) == 300

    def test_strlen(self):
        s = rstr.mallocstr(300)
        ops = """
        [p0]
        i1 = strlen(p0)
        finish(i1)
        """
        self.interpret(ops, [s])
        assert self.getint(0) == 300

    def test_len_of_newstr(self):
        ops = """
        []
        p0 = newstr(300)
        finish(p0)
        """
        self.interpret(ops, [])
        string = self.getptr(0, lltype.Ptr(rstr.STR))
        assert len(string.chars) == 300
