def test_green_send(self): myjitdriver = JitDriver(greens=['i'], reds=['counter']) lst = ["123", "45"] def f(i): counter = 20 c = 0 while counter > 0: myjitdriver.can_enter_jit(counter=counter, i=i) myjitdriver.jit_merge_point(counter=counter, i=i) s = lst[i] c = len(s) counter -= 1 return c res = self.meta_interp(f, [1]) assert res == 2 self.check_resops({ 'jump': 1, 'guard_true': 2, 'int_gt': 2, 'int_sub': 2 }) # all folded away
def test_constfold_pure_oosend(self): myjitdriver = JitDriver(greens=[], reds=['i', 'obj']) class A: @elidable def foo(self): return 42 def fn(n, i): res = 0 obj = A() while i > 0: myjitdriver.can_enter_jit(i=i, obj=obj) myjitdriver.jit_merge_point(i=i, obj=obj) promote(obj) res = obj.foo() i -= 1 return res policy = StopAtXPolicy(A.foo.im_func) res = self.meta_interp(fn, [1, 20], policy=policy) assert res == 42 self.check_resops(call=0)
def test_red_builtin_send(self): myjitdriver = JitDriver(greens=[], reds=['i', 'counter']) lst = [{1: 1, 2: 2, 3: 3}, {4: 4, 5: 5}] def externfn(i): return lst[i] def f(i): counter = 20 res = 0 while counter > 0: myjitdriver.can_enter_jit(counter=counter, i=i) myjitdriver.jit_merge_point(counter=counter, i=i) dct = externfn(i) res = len(dct) counter -= 1 return res res = self.meta_interp(f, [1], policy=StopAtXPolicy(externfn)) assert res == 2 # 'len' becomes a getfield('num_items') for now in lltype, # which is itself encoded as a 'getfield_gc' self.check_resops(call=2, getfield_gc=2)
def test_set_param(self): myjitdriver = JitDriver(greens=[], reds=['n', 'x']) def g(n): x = 0 while n > 0: myjitdriver.can_enter_jit(n=n, x=x) myjitdriver.jit_merge_point(n=n, x=x) n -= 1 x += n return x def f(n, threshold): myjitdriver.set_param('threshold', threshold) return g(n) res = self.meta_interp(f, [10, 3]) assert res == 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 self.check_tree_loop_count(1) res = self.meta_interp(f, [10, 13]) assert res == 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 self.check_tree_loop_count(0)
def test_confirm_enter_jit(self): def confirm_enter_jit(x, y): return x <= 5 myjitdriver = JitDriver(greens=['x'], reds=['y'], confirm_enter_jit=confirm_enter_jit) def f(x, y): while y >= 0: myjitdriver.can_enter_jit(x=x, y=y) myjitdriver.jit_merge_point(x=x, y=y) y -= x return y # res = self.meta_interp(f, [10, 84]) assert res == -6 self.check_loop_count(0) # res = self.meta_interp(f, [3, 19]) assert res == -2 self.check_loop_count(1)
def test_two_behaviors(self): py.test.skip("XXX fix me!!!!!!! problem in optimize.py") myjitdriver = JitDriver(greens = [], reds = ['x', 'y']) class Int: def __init__(self, value): self.value = value cases = [True]*100 + [False, True]*10 + [False]*20 def f(y): x = Int(0) while y > 0: myjitdriver.can_enter_jit(x=x, y=y) myjitdriver.jit_merge_point(x=x, y=y) y -= 1 if cases[y]: x = Int(x.value + 1) return x.value res = self.meta_interp(f, [len(cases)]) assert res == 110 # The generated loops don't contain a new_with_vtable at all. This # is true if we replace "if cases[y]" above with "if not cases[y]" # -- so there is no good reason that it fails. self.check_loops(new_with_vtable=0) self.check_loop_count(2)
def test_constfold_pure_oosend(self): myjitdriver = JitDriver(greens=[], reds = ['i', 'obj']) class A: @purefunction def foo(self): return 42 def fn(n, i): res = 0 obj = A() while i > 0: myjitdriver.can_enter_jit(i=i, obj=obj) myjitdriver.jit_merge_point(i=i, obj=obj) obj = hint(obj, promote=True) res = obj.foo() i-=1 return res policy = StopAtXPolicy(A.foo.im_func) res = self.meta_interp(fn, [1, 20], policy=policy) assert res == 42 if self.type_system == 'ootype': self.check_loops(oosend=0) else: self.check_loops(call=0)
def test_guards_and_holes(self): class A(object): def __init__(self, x): self.x = x mydriver = JitDriver(reds = ['n', 'tot'], greens = []) def f(n): tot = 0 while n > 0: mydriver.can_enter_jit(n=n, tot=tot) mydriver.jit_merge_point(n=n, tot=tot) a = A(n) b = A(n+1) if n % 9 == 0: tot += (a.x + b.x) % 3 c = A(n+1) if n % 10 == 0: tot -= (c.x + a.x) % 3 n -= 1 return tot r = self.meta_interp(f, [70]) expected = f(70) assert r == expected
def test_two_loops_with_virtual(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'node']) def f(n): node = self._new() node.value = 0 node.extra = 0 while n > 0: myjitdriver.can_enter_jit(n=n, node=node) myjitdriver.jit_merge_point(n=n, node=node) next = self._new() next.value = node.value + n next.extra = node.extra + 1 if next.extra == 5: next.value += 100 next.extra = 0 node = next n -= 1 return node.value res = self.meta_interp(f, [18]) assert res == f(18) self.check_trace_count(2) self.check_resops(new_with_vtable=0, setfield_gc=0, getfield_gc=2, new=0)
def test_virtual_loop_invariant_getitem(self): mydriver = JitDriver(reds = ['i', 'sa', 'n', 'node1', 'node2'], greens = []) class A(object): def __init__(self, v1, v2): self.v1 = v1 self.v2 = v2 def f(n): i = sa = 0 node1 = A(1, 2) node2 = A(n, n) while i < n: mydriver.jit_merge_point(i=i, sa=sa, n=n, node1=node1, node2=node2) sa += node1.v1 + node2.v1 + node2.v2 if i < n/2: node1 = A(node2.v1, 2) else: node1 = A(i, 2) i += 1 return sa res = self.meta_interp(f, [16]) assert res == f(16) self.check_resops(getfield_gc=7)
def test_boxed_virtual_string_not_surviving(self): class StrBox(object): def __init__(self, val): self.val = val class IntBox(object): def __init__(self, val): self.val = val _str = self._str mydriver = JitDriver(reds=['i', 'nt', 'sa'], greens=[]) def f(c): nt = StrBox(_str(c * 16)) sa = StrBox(_str('')) i = IntBox(0) while i.val < len(nt.val): mydriver.jit_merge_point(i=i, nt=nt, sa=sa) sa = StrBox(sa.val + StrBox(nt.val[i.val]).val) i = IntBox(i.val + 1) return len(sa.val) assert self.meta_interp(f, ['a']) == f('a')
def test_three_receivers(self): myjitdriver = JitDriver(greens=[], reds=['y']) class Base: pass class W1(Base): def foo(self): return 1 class W2(Base): def foo(self): return 2 class W3(Base): def foo(self): return 3 def externfn(y): if y % 4 == 0: return W1() elif y % 4 == 3: return W2() else: return W3() def f(y): while y > 0: myjitdriver.can_enter_jit(y=y) myjitdriver.jit_merge_point(y=y) w = externfn(y) w.foo() y -= 1 return 42 policy = StopAtXPolicy(externfn) for j in range(69, 75): res = self.meta_interp(f, [j], policy=policy) assert res == 42 self.check_loop_count(3)
def test_blackhole_should_synchronize(self): myjitdriver = JitDriver(greens=[], reds=['frame'], virtualizables=['frame']) class Frame(object): _virtualizable2_ = ['x', 'y'] def __init__(self, x, y): self.x = x self.y = y class SomewhereElse: pass somewhere_else = SomewhereElse() def g(frame): assert frame.x == 2 assert frame.y == 52 frame.y += 100 def f(n): frame = Frame(n, 0) somewhere_else.top_frame = frame # escapes while frame.x > 0: myjitdriver.can_enter_jit(frame=frame) myjitdriver.jit_merge_point(frame=frame) if frame.x == 2: g(frame) frame.y += frame.x frame.x -= 1 return somewhere_else.top_frame.y res = self.meta_interp(f, [10]) assert res == 155 self.check_loops(getfield_gc=0, setfield_gc=0)
def test_behavior_change_after_a_while(self): myjitdriver = JitDriver(greens=[], reds=['y', 'x']) class Base: def __init__(self, value): self.value = value class Int1(Base): pass class Int2(Base): pass cases = [False, True, True, True, True] * 40 def f(y): x = Int1(0) while y > 0: myjitdriver.can_enter_jit(x=x, y=y) myjitdriver.jit_merge_point(x=x, y=y) y -= 1 value = x.value + 1 if cases[y]: x = Int1(value) else: x = Int2(value) return x.value res = self.meta_interp(f, [len(cases)]) assert res == 200 # we expect 2 versions of the loop, 1 entry bridge, # and 1 bridge going from the # loop back to the loop self.check_trace_count(2) # preamble/loop and 1 bridge self.check_jitcell_token_count(1) self.check_target_token_count(3) # preamble, Int1, Int2 self.check_aborted_count(0)
def test_retrace_not_matching_bridge_str(self): @dont_look_inside def external(node): return node.value + 1 myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'node', 'node2', 's']) class A(): def new(self): return A() def val(self, i): return i + 7 class B(A): def new(self): return B() def val(self, i): return i + 42 def f(n): s = '*' * n node = self._new() node2 = A() node.value = 0 i = 0 while i < n: myjitdriver.jit_merge_point(n=n, i=i, node=node, node2=node2, s=s) next = self._new() next.value = node.value + n + node2.val(i) if i != 7: next.value += external(next) else: node2 = B() node = next node2 = node2.new() node.value += len(s) i += 1 return node.value res = self.meta_interp(f, [10], repeat=10) assert res == f(10) self.check_resops(jump=2)
def test_send_to_single_target_method(self): myjitdriver = JitDriver(greens=[], reds=['i', 'counter']) class Foo: def meth(self, y): return self.x + y def externfn(i): foo = Foo() foo.x = i * 42 return foo def f(i): counter = 20 res = 0 while counter > 0: myjitdriver.can_enter_jit(counter=counter, i=i) myjitdriver.jit_merge_point(counter=counter, i=i) foo = externfn(i) res = foo.meth(i) counter -= 1 return res res = self.meta_interp(f, [1], policy=StopAtXPolicy(externfn), backendopt=True) assert res == 43 self.check_loops({ 'call': 1, 'guard_no_exception': 1, 'getfield_gc': 1, 'int_add': 1, 'jump': 1, 'int_gt': 1, 'guard_true': 1, 'int_sub': 1 })
def test_recurse_during_blackholing(self): # this passes, if the blackholing shortcut for calls is turned off # it fails, it is very delicate in terms of parameters, # bridge/loop creation order def p(pc, code): code = hlstr(code) return "%s %d %s" % (code, pc, code[pc]) myjitdriver = JitDriver(greens=['pc', 'code'], reds=['n'], get_printable_location=p) def f(code, n): pc = 0 while pc < len(code): myjitdriver.jit_merge_point(n=n, code=code, pc=pc) op = code[pc] if op == "-": n -= 1 elif op == "c": if n < 70 and n % 3 == 1: n = f("--", n) elif op == "l": if n > 0: myjitdriver.can_enter_jit(n=n, code=code, pc=0) pc = 0 continue else: assert 0 pc += 1 return n def main(n): set_param(None, 'threshold', 3) set_param(None, 'trace_eagerness', 5) return f("c-l", n) expected = main(100) res = self.meta_interp(main, [100], enable_opts='', inline=True) assert res == expected
def test_simple_force_always(self): myjitdriver = JitDriver(greens = [], reds = ['n']) # A = lltype.GcArray(lltype.Signed) class XY: pass class ExCtx: pass exctx = ExCtx() # @dont_look_inside def externalfn(n): m = exctx.topframeref().n assert m == n return 1 # def f(n): while n > 0: myjitdriver.can_enter_jit(n=n) myjitdriver.jit_merge_point(n=n) xy = XY() xy.next1 = lltype.malloc(A, 0) xy.next2 = lltype.malloc(A, 0) xy.next3 = lltype.malloc(A, 0) xy.n = n exctx.topframeref = virtual_ref(xy) n -= externalfn(n) xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) virtual_ref_finish(xy) exctx.topframeref = vref_None # self.meta_interp(f, [15]) self.check_loops(new_with_vtable=2, # XY(), the vref new_array=3) # next1/2/3 self.check_aborted_count(0)
def test_resop_interface(self): driver = JitDriver(greens=[], reds=['i']) def loop(i): while i > 0: driver.jit_merge_point(i=i) i -= 1 def main(): loop(1) op = jit_hooks.resop_new( rop.INT_ADD, [jit_hooks.boxint_new(3), jit_hooks.boxint_new(4)], jit_hooks.boxint_new(1)) assert hlstr(jit_hooks.resop_getopname(op)) == 'int_add' assert jit_hooks.resop_getopnum(op) == rop.INT_ADD box = jit_hooks.resop_getarg(op, 0) assert jit_hooks.box_getint(box) == 3 box2 = jit_hooks.box_clone(box) assert box2 != box assert jit_hooks.box_getint(box2) == 3 assert not jit_hooks.box_isconst(box2) box3 = jit_hooks.box_constbox(box) assert jit_hooks.box_getint(box) == 3 assert jit_hooks.box_isconst(box3) box4 = jit_hooks.box_nonconstbox(box) assert not jit_hooks.box_isconst(box4) box5 = jit_hooks.boxint_new(18) jit_hooks.resop_setarg(op, 0, box5) assert jit_hooks.resop_getarg(op, 0) == box5 box6 = jit_hooks.resop_getresult(op) assert jit_hooks.box_getint(box6) == 1 jit_hooks.resop_setresult(op, box5) assert jit_hooks.resop_getresult(op) == box5 self.meta_interp(main, [])
def test_three_nested_loops(self): myjitdriver = JitDriver(greens=['i'], reds=['x']) bytecode = ".+357" def f(x): assert x >= 0 i = 0 while i < len(bytecode): myjitdriver.jit_merge_point(i=i, x=x) op = bytecode[i] if op == '+': x += 1 elif op == '.': pass elif op == '3': if x % 3 != 0: i -= 1 myjitdriver.can_enter_jit(i=i, x=x) continue elif op == '5': if x % 5 != 0: i -= 2 myjitdriver.can_enter_jit(i=i, x=x) continue elif op == '7': if x % 7 != 0: i -= 4 myjitdriver.can_enter_jit(i=i, x=x) continue i += 1 return x expected = f(0) assert expected == 3 * 5 * 7 res = self.meta_interp(f, [0]) assert res == expected
def test_nested_loops(self): myjitdriver = JitDriver(greens=['i'], reds=['x', 'y']) bytecode = "abc<de" def f(x, y): i = 0 op = '-' while True: myjitdriver.jit_merge_point(i=i, x=x, y=y) op = bytecode[i] if op == 'a': x += 1 elif op == 'b': x += y elif op == 'c': y -= 1 elif op == '<': if y: i -= 2 myjitdriver.can_enter_jit(i=i, x=x, y=y) continue elif op == 'd': y = x elif op == 'e': if x > 1000: break else: i = 0 myjitdriver.can_enter_jit(i=i, x=x, y=y) continue i += 1 return x expected = f(2, 3) res = self.meta_interp(f, [2, 3]) assert res == expected
def test_directly_call_assembler_virtualizable(self): class Thing(object): def __init__(self, val): self.val = val class Frame(object): _virtualizable2_ = ['thing'] driver = JitDriver(greens=['codeno'], reds=['frame', 'i'], virtualizables=['frame'], get_printable_location=lambda codeno: str(codeno), can_inline=lambda codeno: False) def main(codeno): frame = Frame() frame.thing = Thing(0) portal(codeno, frame) return frame.thing.val def portal(codeno, frame): i = 0 while i < 10: driver.can_enter_jit(frame=frame, codeno=codeno, i=i) driver.jit_merge_point(frame=frame, codeno=codeno, i=i) nextval = frame.thing.val if codeno == 0: subframe = Frame() subframe.thing = Thing(nextval) nextval = portal(1, subframe) frame.thing = Thing(nextval + 1) i += 1 return frame.thing.val res = self.meta_interp(main, [0], inline=True) assert res == main(0)
def test_bug_1(self): myjitdriver = JitDriver(greens=[], reds=['n', 'i', 'stack']) def opaque(n, i): if n == 1 and i == 19: for j in range(20): res = f(0) # recurse repeatedly, 20 times assert res == 0 def f(n): stack = [n] i = 0 while i < 20: myjitdriver.can_enter_jit(n=n, i=i, stack=stack) myjitdriver.jit_merge_point(n=n, i=i, stack=stack) opaque(n, i) i += 1 return stack.pop() res = self.meta_interp(f, [1], optimizer=OPTIMIZER_SIMPLE, repeat=2, policy=StopAtXPolicy(opaque)) assert res == 1
def test_directly_call_assembler_fail_guard(self): driver = JitDriver(greens=['codeno'], reds=['i', 'k'], get_printable_location=lambda codeno: str(codeno), can_inline=lambda codeno: False) def portal(codeno, k): i = 0 while i < 10: driver.can_enter_jit(codeno=codeno, i=i, k=k) driver.jit_merge_point(codeno=codeno, i=i, k=k) if codeno == 2: k += portal(1, k) elif k > 40: if i % 2: k += 1 else: k += 2 k += 1 i += 1 return k res = self.meta_interp(portal, [2, 0], inline=True) assert res == 13542
def test_cannot_use_invalid_virtualref(self): myjitdriver = JitDriver(greens=[], reds=['n']) # class XY: n = 0 # def fn(n): res = False while n > 0: myjitdriver.can_enter_jit(n=n) myjitdriver.jit_merge_point(n=n) xy = XY() xy.n = n vref = virtual_ref(xy) virtual_ref_finish(vref, xy) vref() # raises InvalidVirtualRef when jitted n -= 1 return res # py.test.raises(InvalidVirtualRef, "fn(10)") py.test.raises(LLException, "self.meta_interp(fn, [10])")
def test_loop_header(self): # artificial test: we enter into the JIT only when can_enter_jit() # is seen, but we close a loop in the JIT much more quickly # because of loop_header(). mydriver = JitDriver(reds=['n', 'm'], greens=[]) def f(m): n = 0 while True: mydriver.jit_merge_point(n=n, m=m) if n > m: m -= 1 if m < 0: return n n = 0 mydriver.can_enter_jit(n=n, m=m) else: n += 1 mydriver.loop_header() assert f(15) == 1 res = self.meta_interp(f, [15], backendopt=True) assert res == 1 self.check_resops(int_add=2) # I get 13 without the loop_header()
def test_trace_from_start_always(self): from pypy.rlib.nonconst import NonConstant driver = JitDriver(greens = ['c'], reds = ['i', 'v']) def portal(c, i, v): while i > 0: driver.jit_merge_point(c=c, i=i, v=v) portal(c, i - 1, v) if v: driver.can_enter_jit(c=c, i=i, v=v) break def main(c, i, _set_param, v): if _set_param: set_param(driver, 'function_threshold', 0) portal(c, i, v) self.meta_interp(main, [10, 10, False, False], inline=True) self.check_jitcell_token_count(1) self.check_trace_count(1) self.meta_interp(main, [3, 10, True, False], inline=True) self.check_jitcell_token_count(0) self.check_trace_count(0)
def test_oosends_not_followed(self): myjitdriver = JitDriver(greens = [], reds = ['n']) class A: def meth(self): return 42 class B(A): def meth(self): return 45 class C(A): def meth(self): return 64 def f(n): while n > 0: myjitdriver.can_enter_jit(n=n) myjitdriver.jit_merge_point(n=n) n -= 1 if n < 0: x = B() else: x = C() return x.meth() res = self.meta_interp(f, [7]) assert res == 64 assert self.seen_frames == ['f', 'f']
def test_external_exception_handling_translates(self): jitdriver = JitDriver(greens=[], reds=['n', 'total']) @dont_look_inside def f(x): if x > 20: return 2 raise ValueError @dont_look_inside def g(x): if x > 15: raise ValueError return 2 def main(i): jitdriver.set_param("threshold", 3) jitdriver.set_param("trace_eagerness", 2) total = 0 n = i while n > 3: jitdriver.can_enter_jit(n=n, total=total) jitdriver.jit_merge_point(n=n, total=total) try: total += f(n) except ValueError: total += 1 try: total += g(n) except ValueError: total -= 1 n -= 1 return total * 10 res = self.meta_interp(main, [40]) assert res == main(40)
def test_simple_no_access(self): myjitdriver = JitDriver(greens = [], reds = ['n']) # A = lltype.GcArray(lltype.Signed) class XY: pass class ExCtx: pass exctx = ExCtx() # @dont_look_inside def externalfn(n): if n > 1000: return compute_unique_id(exctx.topframeref()) return 1 # def f(n): while n > 0: myjitdriver.can_enter_jit(n=n) myjitdriver.jit_merge_point(n=n) xy = XY() xy.next1 = lltype.malloc(A, 0) xy.next2 = lltype.malloc(A, 0) xy.next3 = lltype.malloc(A, 0) exctx.topframeref = virtual_ref(xy) n -= externalfn(n) exctx.topframeref = vref_None xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) virtual_ref_finish(xy) # self.meta_interp(f, [15]) self.check_loops(new_with_vtable=2, # the vref, and xy so far, new_array=0) # but not xy.next1/2/3 self.check_aborted_count(0)