def __init__(self, translator, policy=None, backendopt=True, CPUClass=None, ProfilerClass=EmptyProfiler, **kwds): pyjitpl._warmrunnerdesc = self # this is a global for debugging only! self.set_translator(translator) self.memory_manager = memmgr.MemoryManager() self.build_cpu(CPUClass, **kwds) self.inline_inlineable_portals() self.find_portals() self.codewriter = codewriter.CodeWriter(self.cpu, self.jitdrivers_sd) if policy is None: policy = JitPolicy() policy.set_supports_floats(self.cpu.supports_floats) policy.set_supports_longlong(self.cpu.supports_longlong) policy.set_supports_singlefloats(self.cpu.supports_singlefloats) graphs = self.codewriter.find_all_graphs(policy) policy.dump_unsafe_loops() self.check_access_directly_sanity(graphs) if backendopt: self.prejit_optimizations(policy, graphs) elif self.opt.listops: self.prejit_optimizations_minimal_inline(policy, graphs) self.build_meta_interp( ProfilerClass, translator.config.translation.jit_opencoder_model) self.make_args_specifications() # from rpython.jit.metainterp.virtualref import VirtualRefInfo vrefinfo = VirtualRefInfo(self) self.codewriter.setup_vrefinfo(vrefinfo) # from rpython.jit.metainterp import counter if self.cpu.translate_support_code: self.jitcounter = counter.JitCounter(translator=translator) else: self.jitcounter = counter.DeterministicJitCounter() # self.hooks = policy.jithookiface self.make_virtualizable_infos() self.make_driverhook_graphs() self.make_enter_functions() self.rewrite_jit_merge_points(policy) verbose = False # not self.cpu.translate_support_code self.rewrite_access_helpers() self.create_jit_entry_points() jitcodes = self.codewriter.make_jitcodes(verbose=verbose) self.metainterp_sd.jitcodes = jitcodes self.rewrite_can_enter_jits() self.rewrite_set_param_and_get_stats() self.rewrite_force_virtual(vrefinfo) self.rewrite_jitcell_accesses() self.rewrite_force_quasi_immutable() self.add_finish() self.metainterp_sd.finish_setup(self.codewriter)
class LLtypeMixin(object): node_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) node_vtable.name = rclass.alloc_array_name('node') node_vtable2 = lltype.malloc(OBJECT_VTABLE, immortal=True) node_vtable2.name = rclass.alloc_array_name('node2') node_vtable3 = lltype.malloc(OBJECT_VTABLE, immortal=True) node_vtable3.name = rclass.alloc_array_name('node3') node_vtable3.subclassrange_min = 3 node_vtable3.subclassrange_max = 3 cpu = runner.LLGraphCPU(None) NODE = lltype.GcForwardReference() S = lltype.GcForwardReference() NODE.become( lltype.GcStruct('NODE', ('parent', OBJECT), ('value', lltype.Signed), ('floatval', lltype.Float), ('charval', lltype.Char), ('nexttuple', lltype.Ptr(S)), ('next', lltype.Ptr(NODE)))) S.become( lltype.GcStruct('TUPLE', ('a', lltype.Signed), ('abis', lltype.Signed), ('b', lltype.Ptr(NODE)))) NODE2 = lltype.GcStruct('NODE2', ('parent', NODE), ('other', lltype.Ptr(NODE))) NODE3 = lltype.GcForwardReference() NODE3.become( lltype.GcStruct('NODE3', ('parent', OBJECT), ('value', lltype.Signed), ('next', lltype.Ptr(NODE3)), hints={'immutable': True})) big_fields = [('big' + i, lltype.Signed) for i in string.ascii_lowercase] BIG = lltype.GcForwardReference() BIG.become(lltype.GcStruct('BIG', *big_fields, hints={'immutable': True})) for field, _ in big_fields: locals()[field + 'descr'] = cpu.fielddescrof(BIG, field) node = lltype.malloc(NODE) node.value = 5 node.next = node node.parent.typeptr = node_vtable nodeaddr = lltype.cast_opaque_ptr(llmemory.GCREF, node) #nodebox = InputArgRef(lltype.cast_opaque_ptr(llmemory.GCREF, node)) node2 = lltype.malloc(NODE2) node2.parent.parent.typeptr = node_vtable2 node2addr = lltype.cast_opaque_ptr(llmemory.GCREF, node2) myptr = lltype.cast_opaque_ptr(llmemory.GCREF, node) mynodeb = lltype.malloc(NODE) myarray = lltype.cast_opaque_ptr( llmemory.GCREF, lltype.malloc(lltype.GcArray(lltype.Signed), 13, zero=True)) mynodeb.parent.typeptr = node_vtable myptrb = lltype.cast_opaque_ptr(llmemory.GCREF, mynodeb) myptr2 = lltype.malloc(NODE2) myptr2.parent.parent.typeptr = node_vtable2 myptr2 = lltype.cast_opaque_ptr(llmemory.GCREF, myptr2) nullptr = lltype.nullptr(llmemory.GCREF.TO) mynode3 = lltype.malloc(NODE3) mynode3.parent.typeptr = node_vtable3 mynode3.value = 7 mynode3.next = mynode3 myptr3 = lltype.cast_opaque_ptr(llmemory.GCREF, mynode3) # a NODE2 mynode4 = lltype.malloc(NODE3) mynode4.parent.typeptr = node_vtable3 myptr4 = lltype.cast_opaque_ptr(llmemory.GCREF, mynode4) # a NODE3 nodesize = cpu.sizeof(NODE, node_vtable) node_tid = nodesize.get_type_id() nodesize2 = cpu.sizeof(NODE2, node_vtable2) nodesize3 = cpu.sizeof(NODE3, node_vtable3) valuedescr = cpu.fielddescrof(NODE, 'value') floatdescr = cpu.fielddescrof(NODE, 'floatval') chardescr = cpu.fielddescrof(NODE, 'charval') nextdescr = cpu.fielddescrof(NODE, 'next') nexttupledescr = cpu.fielddescrof(NODE, 'nexttuple') otherdescr = cpu.fielddescrof(NODE2, 'other') valuedescr3 = cpu.fielddescrof(NODE3, 'value') nextdescr3 = cpu.fielddescrof(NODE3, 'next') assert valuedescr3.is_always_pure() assert nextdescr3.is_always_pure() 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, None) quasi = lltype.malloc(QUASI, immortal=True) quasi.inst_field = -4247 quasifielddescr = cpu.fielddescrof(QUASI, 'inst_field') quasiptr = lltype.cast_opaque_ptr(llmemory.GCREF, quasi) quasiimmutdescr = QuasiImmutDescr(cpu, quasiptr, 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') immut = lltype.malloc(INTOBJ_IMMUT, zero=True) immutaddr = lltype.cast_opaque_ptr(llmemory.GCREF, immut) noimmut_descr = cpu.sizeof(INTOBJ_NOIMMUT, intobj_noimmut_vtable) immut_descr = cpu.sizeof(INTOBJ_IMMUT, intobj_immut_vtable) PTROBJ_IMMUT = lltype.GcStruct('PTROBJ_IMMUT', ('parent', OBJECT), ('ptrval', lltype.Ptr(OBJECT)), hints={'immutable': True}) ptrobj_immut_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) ptrobj_immut_descr = cpu.sizeof(PTROBJ_IMMUT, ptrobj_immut_vtable) immut_ptrval = cpu.fielddescrof(PTROBJ_IMMUT, 'ptrval') arraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Signed)) int32arraydescr = cpu.arraydescrof(lltype.GcArray(rffi.INT)) int16arraydescr = cpu.arraydescrof(lltype.GcArray(rffi.SHORT)) float32arraydescr = cpu.arraydescrof(lltype.GcArray(lltype.SingleFloat)) arraydescr_tid = arraydescr.get_type_id() array = lltype.malloc(lltype.GcArray(lltype.Signed), 15, zero=True) arrayref = lltype.cast_opaque_ptr(llmemory.GCREF, array) array2 = lltype.malloc(lltype.GcArray(lltype.Ptr(S)), 15, zero=True) array2ref = lltype.cast_opaque_ptr(llmemory.GCREF, array2) gcarraydescr = cpu.arraydescrof(lltype.GcArray(llmemory.GCREF)) gcarraydescr_tid = gcarraydescr.get_type_id() floatarraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Float)) arrayimmutdescr = cpu.arraydescrof( lltype.GcArray(lltype.Signed, hints={"immutable": True})) immutarray = lltype.cast_opaque_ptr( llmemory.GCREF, lltype.malloc(arrayimmutdescr.A, 13, zero=True)) gcarrayimmutdescr = cpu.arraydescrof( lltype.GcArray(llmemory.GCREF, hints={"immutable": True})) floatarrayimmutdescr = cpu.arraydescrof( lltype.GcArray(lltype.Float, hints={"immutable": True})) # a GcStruct not inheriting from OBJECT tpl = lltype.malloc(S, zero=True) tupleaddr = lltype.cast_opaque_ptr(llmemory.GCREF, tpl) nodefull2 = lltype.malloc(NODE, zero=True) nodefull2addr = lltype.cast_opaque_ptr(llmemory.GCREF, nodefull2) ssize = cpu.sizeof(S, None) adescr = cpu.fielddescrof(S, 'a') abisdescr = cpu.fielddescrof(S, 'abis') 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))))) W_ROOT = lltype.GcStruct( 'W_ROOT', ('parent', OBJECT), ('inst_w_seq', llmemory.GCREF), ('inst_index', lltype.Signed), ('inst_w_list', llmemory.GCREF), ('inst_length', lltype.Signed), ('inst_start', lltype.Signed), ('inst_step', lltype.Signed)) inst_w_seq = cpu.fielddescrof(W_ROOT, 'inst_w_seq') inst_index = cpu.fielddescrof(W_ROOT, 'inst_index') inst_length = cpu.fielddescrof(W_ROOT, 'inst_length') inst_start = cpu.fielddescrof(W_ROOT, 'inst_start') inst_step = cpu.fielddescrof(W_ROOT, 'inst_step') inst_w_list = cpu.fielddescrof(W_ROOT, 'inst_w_list') w_root_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) tsize = cpu.sizeof(T, None) cdescr = cpu.fielddescrof(T, 'c') ddescr = cpu.fielddescrof(T, 'd') arraydescr3 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(NODE3))) U = lltype.GcStruct('U', ('parent', OBJECT), ('one', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE))))) u_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) SIMPLE = lltype.GcStruct('simple', ('parent', OBJECT), ('value', lltype.Signed)) simplevalue = cpu.fielddescrof(SIMPLE, 'value') simple_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) simpledescr = cpu.sizeof(SIMPLE, simple_vtable) simple = lltype.malloc(SIMPLE, zero=True) simpleaddr = lltype.cast_opaque_ptr(llmemory.GCREF, simple) #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) elidablecalldescr = cpu.calldescrof( FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([valuedescr], [], [], [valuedescr], [], [], EffectInfo.EF_ELIDABLE_CANNOT_RAISE)) elidable2calldescr = cpu.calldescrof( FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([valuedescr], [], [], [valuedescr], [], [], EffectInfo.EF_ELIDABLE_OR_MEMORYERROR)) elidable3calldescr = cpu.calldescrof( FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([valuedescr], [], [], [valuedescr], [], [], EffectInfo.EF_ELIDABLE_CAN_RAISE)) 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], [])) writevalue3descr = cpu.calldescrof( FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [valuedescr3], [], [])) 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)) raw_malloc_descr = cpu.calldescrof( FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], [], [], EffectInfo.EF_CAN_RAISE, oopspecindex=EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR)) raw_free_descr = cpu.calldescrof( FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=EffectInfo.OS_RAW_FREE)) chararray = lltype.GcArray(lltype.Char) chararraydescr = cpu.arraydescrof(chararray) u2array = lltype.GcArray(rffi.USHORT) u2arraydescr = cpu.arraydescrof(u2array) nodefull = lltype.malloc(NODE2, zero=True) nodefull.parent.next = lltype.cast_pointer(lltype.Ptr(NODE), nodefull) nodefull.parent.nexttuple = tpl nodefulladdr = lltype.cast_opaque_ptr(llmemory.GCREF, nodefull) # 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") complexarraycopydescr = cpu.calldescrof( FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [complexarraydescr], [], [], [complexarraydescr], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=EffectInfo.OS_ARRAYCOPY)) rawarraydescr = cpu.arraydescrof( lltype.Array(lltype.Signed, hints={'nolength': True})) rawarraydescr_char = cpu.arraydescrof( lltype.Array(lltype.Char, hints={'nolength': True})) rawarraydescr_float = cpu.arraydescrof( lltype.Array(lltype.Float, hints={'nolength': True})) fc_array = lltype.GcArray( lltype.Struct("floatchar", ("float", lltype.Float), ("char", lltype.Char))) fc_array_descr = cpu.arraydescrof(fc_array) fc_array_floatdescr = cpu.interiorfielddescrof(fc_array, "float") fc_array_chardescr = cpu.interiorfielddescrof(fc_array, "char") 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'), ]: if _name in ('strconcatdescr', 'strslicedescr'): _extra = EffectInfo.EF_ELIDABLE_OR_MEMORYERROR else: _extra = EffectInfo.EF_ELIDABLE_CANNOT_RAISE _oopspecindex = getattr(EffectInfo, _os) locals()[_name] = \ cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], [], [], _extra, oopspecindex=_oopspecindex)) # _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI')) locals()[_name.replace('str', 'unicode')] = \ cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], [], [], _extra, oopspecindex=_oopspecindex)) s2u_descr = cpu.calldescrof( FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], [], [], EffectInfo.EF_ELIDABLE_CAN_RAISE, oopspecindex=EffectInfo.OS_STR2UNICODE)) # class LoopToken(AbstractDescr): pass asmdescr = LoopToken() # it can be whatever, it's not a descr though class FakeWarmRunnerDesc: pass FakeWarmRunnerDesc.cpu = cpu vrefinfo = VirtualRefInfo(FakeWarmRunnerDesc) virtualtokendescr = vrefinfo.descr_virtual_token virtualforceddescr = vrefinfo.descr_forced FUNC = lltype.FuncType([], lltype.Void) ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, can_invalidate=False, oopspecindex=EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE) clear_vable = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, ei) jit_virtual_ref_vtable = vrefinfo.jit_virtual_ref_vtable vref_descr = cpu.sizeof(vrefinfo.JIT_VIRTUAL_REF, jit_virtual_ref_vtable) FUNC = lltype.FuncType([lltype.Signed, lltype.Signed], lltype.Signed) ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_ELIDABLE_CANNOT_RAISE, can_invalidate=False, oopspecindex=EffectInfo.OS_INT_PY_DIV) int_py_div_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, ei) ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_ELIDABLE_CANNOT_RAISE, can_invalidate=False, oopspecindex=EffectInfo.OS_INT_UDIV) int_udiv_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, ei) ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_ELIDABLE_CANNOT_RAISE, can_invalidate=False, oopspecindex=EffectInfo.OS_INT_PY_MOD) int_py_mod_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, ei) FUNC = lltype.FuncType([], llmemory.GCREF) ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_ELIDABLE_CAN_RAISE) plain_r_calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, ei) namespace = locals()
def finish_setup_for_interp_operations(self): self.vrefinfo = VirtualRefInfo(self.warmrunnerstate) self.cw.setup_vrefinfo(self.vrefinfo)
class VRefTests(object): def finish_setup_for_interp_operations(self): self.vrefinfo = VirtualRefInfo(self.warmrunnerstate) self.cw.setup_vrefinfo(self.vrefinfo) def test_rewrite_graphs(self): class X: pass def fn(): x = X() vref = virtual_ref(x) x1 = vref() # jit_force_virtual virtual_ref_finish(vref, x) # _get_jitcodes(self, self.CPUClass, fn, []) graph = self.all_graphs[0] assert graph.name == 'fn' self.vrefinfo.replace_force_virtual_with_call([graph]) # def check_call(op, fname): assert op.opname == 'direct_call' assert op.args[0].value._obj._name == fname # ops = [op for block, op in graph.iterblockops()] check_call(ops[-3], 'virtual_ref') check_call(ops[-2], 'force_virtual_if_necessary') check_call(ops[-1], 'virtual_ref_finish') def test_make_vref_simple(self): class X: pass class ExCtx: pass exctx = ExCtx() # def f(): x = X() exctx.topframeref = vref = virtual_ref(x) exctx.topframeref = vref_None virtual_ref_finish(vref, x) return 1 # self.interp_operations(f, []) self.check_operations_history(new_with_vtable=1, # X() virtual_ref=1, virtual_ref_finish=1) def test_make_vref_guard(self): if not isinstance(self, TestLLtype): py.test.skip("purely frontend test") # class FooBarError(Exception): pass class X: def __init__(self, n): self.n = n class ExCtx: _frame = None exctx = ExCtx() # @dont_look_inside def external(n): if exctx._frame is None: raise FooBarError if n > 100: return exctx.topframeref().n return n def enter(n): x = X(n + 10) exctx._frame = x exctx.topframeref = virtual_ref(x) def leave(): vref = exctx.topframeref exctx.topframeref = vref_None virtual_ref_finish(vref, exctx._frame) def f(n): enter(n) n = external(n) # ^^^ the point is that X() and the vref should be kept alive here leave() return n # res = self.interp_operations(f, [5]) assert res == 5 self.check_operations_history(virtual_ref=1, guard_not_forced=1) # ops = self.metainterp.staticdata.stats.loops[0].operations [guard_op] = [op for op in ops if op.getopnum() == rop.GUARD_NOT_FORCED] bxs1 = [box for box in guard_op.getfailargs() if str(box._getrepr_()).endswith('.X')] assert len(bxs1) == 1 bxs2 = [box for box in guard_op.getfailargs() if str(box._getrepr_()).endswith('JitVirtualRef')] assert len(bxs2) == 1 JIT_VIRTUAL_REF = self.vrefinfo.JIT_VIRTUAL_REF FOO = lltype.GcStruct('FOO') foo = lltype.malloc(FOO) tok = lltype.cast_opaque_ptr(llmemory.GCREF, foo) bxs2[0].getref(lltype.Ptr(JIT_VIRTUAL_REF)).virtual_token = tok # # try reloading from blackhole.py's point of view from rpython.jit.metainterp.resume import ResumeDataDirectReader cpu = self.metainterp.cpu cpu.get_int_value = lambda df,i:guard_op.getfailargs()[i].getint() cpu.get_ref_value = lambda df,i:guard_op.getfailargs()[i].getref_base() class FakeMetaInterpSd: callinfocollection = None FakeMetaInterpSd.cpu = cpu resumereader = ResumeDataDirectReader(FakeMetaInterpSd(), guard_op.getdescr(), "deadframe") vrefinfo = self.metainterp.staticdata.virtualref_info lst = [] vrefinfo.continue_tracing = lambda vref, virtual: \ lst.append((vref, virtual)) resumereader.consume_vref_and_vable(vrefinfo, None, None) del vrefinfo.continue_tracing assert len(lst) == 1 lltype.cast_opaque_ptr(lltype.Ptr(JIT_VIRTUAL_REF), lst[0][0]) # assert correct type # # try reloading from pyjitpl's point of view self.metainterp.rebuild_state_after_failure(guard_op.getdescr(), "deadframe") assert len(self.metainterp.framestack) == 1 assert len(self.metainterp.virtualref_boxes) == 2 assert self.metainterp.virtualref_boxes[0].value == bxs1[0].value assert self.metainterp.virtualref_boxes[1].value == bxs2[0].value def test_make_vref_escape_after_finish(self): jitdriver = JitDriver(greens = [], reds = ['n']) # class X: pass class ExCtx: pass exctx = ExCtx() # @dont_look_inside def g(vref): # we cannot do anything with the vref after the call to finish() pass # def f(n): while n > 0: jitdriver.can_enter_jit(n=n) jitdriver.jit_merge_point(n=n) x = X() x.n = n exctx.topframeref = vref = virtual_ref(x) # here, 'x' should be virtual exctx.topframeref = vref_None virtual_ref_finish(vref, x) # 'x' and 'vref' can randomly escape after the call to # finish(). g(vref) n -= 1 return 1 # self.meta_interp(f, [10]) self.check_resops(new_with_vtable=2) # the vref self.check_aborted_count(0) def test_simple_all_removed(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): 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 = vref = 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(vref, xy) # self.meta_interp(f, [15]) self.check_resops(new_with_vtable=0, new_array=0) self.check_aborted_count(0) 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 = vref = 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(vref, xy) # self.meta_interp(f, [15]) self.check_resops(new_with_vtable=2, # the vref: xy doesn't need to be forced new_array=0) # and neither xy.next1/2/3 self.check_aborted_count(0) 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 = vref = virtual_ref(xy) n -= externalfn(n) xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) virtual_ref_finish(vref, xy) exctx.topframeref = vref_None # self.meta_interp(f, [15]) self.check_resops(new_with_vtable=4, # XY(), the vref new_array=6) # next1/2/3 self.check_aborted_count(0) def test_simple_force_sometimes(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 == 13: exctx.m = exctx.topframeref().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) buf = lltype.malloc(rffi.CCHARP.TO, 1, flavor='raw') buf[0] = chr(n) # this is a raw virtual xy.next4 = lltype.malloc(rffi.CCHARP.TO, 1, flavor='raw') xy.n = n exctx.topframeref = vref = virtual_ref(xy) n -= externalfn(n) xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) lltype.free(xy.next4, flavor='raw') xy.next4 = lltype.nullptr(rffi.CCHARP.TO) virtual_ref_finish(vref, xy) exctx.topframeref = vref_None lltype.free(buf, flavor='raw') return exctx.m # res = self.meta_interp(f, [30]) assert res == 13 self.check_resops(new_with_vtable=2, # the vref, but not XY() new_array=0) # and neither next1/2/3 self.check_trace_count(1) self.check_aborted_count(0) def test_blackhole_forces(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): exctx.m = exctx.topframeref().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 = vref = virtual_ref(xy) if n == 13: externalfn(n) n -= 1 exctx.topframeref = vref_None xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) virtual_ref_finish(vref, xy) return exctx.m # res = self.meta_interp(f, [30]) assert res == 13 self.check_resops(new_with_vtable=0, # all virtualized in the n!=13 loop new_array=0) self.check_trace_count(1) self.check_aborted_count(0) def test_bridge_forces(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): exctx.m = exctx.topframeref().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.next4 = lltype.malloc(A, 0) xy.next5 = lltype.malloc(A, 0) xy.n = n exctx.topframeref = vref = virtual_ref(xy) if n % 6 == 0: xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) externalfn(n) n -= 1 exctx.topframeref = vref_None xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) xy.next4 = lltype.nullptr(A) xy.next5 = lltype.nullptr(A) virtual_ref_finish(vref, xy) return exctx.m # res = self.meta_interp(f, [72]) assert res == 6 self.check_trace_count(2) # the loop and the bridge self.check_resops(new_with_vtable=2, # loop: nothing; bridge: vref, xy new_array=2) # bridge: next4, next5 self.check_aborted_count(0) def test_jit_force_virtual_seen(self): myjitdriver = JitDriver(greens=[], reds=['n']) A = lltype.GcArray(lltype.Signed) class XY(object): pass class ExCtx(object): pass exctx = ExCtx() escapes = [] def f(n): while n > 0: myjitdriver.can_enter_jit(n=n) myjitdriver.jit_merge_point(n=n) xy = XY() xy.n = n exctx.topframeref = vref = virtual_ref(xy) escapes.append(xy) xy.next1 = lltype.malloc(A, 0) n = exctx.topframeref().n - 1 exctx.topframeref = vref_None virtual_ref_finish(vref, xy) return 1 # res = self.meta_interp(f, [15]) assert res == 1 self.check_resops(new_with_vtable=2, # xy new_array=2) # next1 self.check_aborted_count(0) def test_recursive_call_1(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'rec', 'frame']) # class XY: pass class ExCtx: pass exctx = ExCtx() # def f(frame, n, reclevel): while n > 0: myjitdriver.can_enter_jit(n=n, frame=frame, rec=reclevel) myjitdriver.jit_merge_point(n=n, frame=frame, rec=reclevel) if reclevel == 0: return n xy = XY() exctx.topframeref = vref = virtual_ref(xy) m = f(xy, n, reclevel-1) assert m == n n -= 1 exctx.topframeref = vref_None virtual_ref_finish(vref, xy) return 2 def main(n, reclevel): return f(XY(), n, reclevel) # res = self.meta_interp(main, [15, 1]) assert res == main(15, 1) self.check_aborted_count(0) def test_recursive_call_2(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'rec', 'frame']) # class XY: n = 0 class ExCtx: pass exctx = ExCtx() # def f(frame, n, reclevel): while n > 0: myjitdriver.can_enter_jit(n=n, frame=frame, rec=reclevel) myjitdriver.jit_merge_point(n=n, frame=frame, rec=reclevel) frame.n += 1 xy = XY() xy.n = n exctx.topframeref = vref = virtual_ref(xy) if reclevel > 0: m = f(xy, frame.n, reclevel-1) assert xy.n == m n -= 1 else: n -= 2 exctx.topframeref = vref_None virtual_ref_finish(vref, xy) return frame.n def main(n, reclevel): return f(XY(), n, reclevel) # res = self.meta_interp(main, [10, 2]) assert res == main(10, 2) self.check_aborted_count(0) def test_alloc_virtualref_and_then_alloc_structure(self): myjitdriver = JitDriver(greens = [], reds = ['n']) # class XY: pass class ExCtx: pass exctx = ExCtx() @dont_look_inside def escapexy(xy): print 'escapexy:', xy.n if xy.n % 5 == 0: vr = exctx.vr print 'accessing via vr:', vr() assert vr() is xy # def f(n): while n > 0: myjitdriver.jit_merge_point(n=n) xy = XY() xy.n = n vr = virtual_ref(xy) # force the virtualref to be allocated exctx.vr = vr # force xy to be allocated escapexy(xy) # clean up exctx.vr = vref_None virtual_ref_finish(vr, xy) n -= 1 return 1 # res = self.meta_interp(f, [15]) assert res == 1 self.check_resops(new_with_vtable=4) # vref, xy 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(UnknownException, "self.meta_interp(fn, [10])") def test_call_virtualref_already_forced(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'res']) # class XY: n = 0 # @dont_look_inside def force_it(vref, n): if n % 6 == 0: return vref().n return 0 def fn(n): res = 0 while n > 0: myjitdriver.can_enter_jit(n=n, res=res) myjitdriver.jit_merge_point(n=n, res=res) xy = XY() xy.n = n vref = virtual_ref(xy) force_it(vref, n) virtual_ref_finish(vref, xy) res += force_it(vref, n) # doesn't raise, because it was already forced n -= 1 return res # assert fn(10) == 6 res = self.meta_interp(fn, [10]) assert res == 6 def test_is_virtual(self): myjitdriver = JitDriver(greens=[], reds=['n', 'res1']) class X: pass @dont_look_inside def residual(vref): return vref.virtual # def f(n): res1 = -42 while n > 0: myjitdriver.jit_merge_point(n=n, res1=res1) x = X() vref = virtual_ref(x) res1 = residual(vref) virtual_ref_finish(vref, x) n -= 1 return res1 # res = self.meta_interp(f, [10]) assert res == 1 def test_is_not_virtual_none(self): myjitdriver = JitDriver(greens=[], reds=['n', 'res1']) @dont_look_inside def residual(vref): return vref.virtual # def f(n): res1 = -42 while n > 0: myjitdriver.jit_merge_point(n=n, res1=res1) res1 = residual(vref_None) n -= 1 return res1 # res = self.meta_interp(f, [10]) assert res == 0 def test_is_not_virtual_non_none(self): myjitdriver = JitDriver(greens=[], reds=['n', 'res1']) class X: pass @dont_look_inside def residual(vref): return vref.virtual # def f(n): res1 = -42 while n > 0: myjitdriver.jit_merge_point(n=n, res1=res1) x = X() res1 = residual(non_virtual_ref(x)) n -= 1 return res1 # res = self.meta_interp(f, [10]) assert res == 0 def test_force_virtual_vref(self): myjitdriver = JitDriver(greens=[], reds=['n', 'ec']) class ExecutionContext(object): pass class Frame(object): def __init__(self, x): self.x = x def f(n): ec = ExecutionContext() while n > 0: myjitdriver.jit_merge_point(n=n, ec=ec) frame = Frame(1) ec.topframeref = virtual_ref(frame) n -= ec.topframeref().x frame_vref = ec.topframeref ec.topframeref = vref_None virtual_ref_finish(frame_vref, frame) return n res = self.meta_interp(f, [10]) assert res == 0 self.check_resops({ 'int_sub': 2, 'int_gt': 2, 'jump': 1, 'guard_true': 2, 'force_token': 2, 'setfield_gc': 1 })
class VRefTests(object): def finish_setup_for_interp_operations(self): self.vrefinfo = VirtualRefInfo(self.warmrunnerstate) self.cw.setup_vrefinfo(self.vrefinfo) def test_rewrite_graphs(self): class X: pass def fn(): x = X() vref = virtual_ref(x) x1 = vref() # jit_force_virtual virtual_ref_finish(vref, x) # _get_jitcodes(self, self.CPUClass, fn, []) graph = self.all_graphs[0] assert graph.name == 'fn' self.vrefinfo.replace_force_virtual_with_call([graph]) # def check_call(op, fname): assert op.opname == 'direct_call' assert op.args[0].value._obj._name.startswith(fname) # ops = [op for block, op in graph.iterblockops()] check_call(ops[-3], 'virtual_ref') check_call(ops[-2], 'force_virtual_if_necessary') check_call(ops[-1], 'virtual_ref_finish') def test_make_vref_simple(self): class X: pass class ExCtx: pass exctx = ExCtx() # def f(): x = X() exctx.topframeref = vref = virtual_ref(x) exctx.topframeref = vref_None virtual_ref_finish(vref, x) return 1 # self.interp_operations(f, []) self.check_operations_history( new_with_vtable=1, # X() virtual_ref=1, virtual_ref_finish=1) def test_make_vref_guard(self): if not isinstance(self, TestLLtype): py.test.skip("purely frontend test") # class FooBarError(Exception): pass class X: def __init__(self, n): self.n = n class ExCtx: _frame = None exctx = ExCtx() # @dont_look_inside def external(n): if exctx._frame is None: raise FooBarError if n > 100: return exctx.topframeref().n return n def enter(n): x = X(n + 10) exctx._frame = x exctx.topframeref = virtual_ref(x) def leave(): vref = exctx.topframeref exctx.topframeref = vref_None virtual_ref_finish(vref, exctx._frame) def f(n): enter(n) n = external(n) # ^^^ the point is that X() and the vref should be kept alive here leave() return n # res = self.interp_operations(f, [5]) assert res == 5 self.check_operations_history(virtual_ref=1, guard_not_forced=1) # ops = self.metainterp.staticdata.stats.loops[0].operations [guard_op ] = [op for op in ops if op.getopnum() == rop.GUARD_NOT_FORCED] bxs1 = [box for box in guard_op.getfailargs() if '.X' in str(box)] assert len(bxs1) == 1 bxs2 = [(i, box) for i, box in enumerate(guard_op.getfailargs()) if 'JitVirtualRef' in str(box)] assert len(bxs2) == 1 JIT_VIRTUAL_REF = self.vrefinfo.JIT_VIRTUAL_REF FOO = lltype.GcStruct('FOO') foo = lltype.malloc(FOO) tok = lltype.cast_opaque_ptr(llmemory.GCREF, foo) cpu = self.metainterp.cpu py.test.skip("rewrite this test") bxs2[0].getref(lltype.Ptr(JIT_VIRTUAL_REF)).virtual_token = tok # # try reloading from blackhole.py's point of view from rpython.jit.metainterp.resume import ResumeDataDirectReader cpu.get_int_value = lambda df, i: guard_op.getfailargs()[i].getint() cpu.get_ref_value = lambda df, i: guard_op.getfailargs()[ i].getref_base() class FakeMetaInterpSd: callinfocollection = None FakeMetaInterpSd.cpu = cpu resumereader = ResumeDataDirectReader(FakeMetaInterpSd(), guard_op.getdescr(), "deadframe") vrefinfo = self.metainterp.staticdata.virtualref_info lst = [] vrefinfo.continue_tracing = lambda vref, virtual: \ lst.append((vref, virtual)) resumereader.consume_vref_and_vable(vrefinfo, None, None) del vrefinfo.continue_tracing assert len(lst) == 1 lltype.cast_opaque_ptr(lltype.Ptr(JIT_VIRTUAL_REF), lst[0][0]) # assert correct type # # try reloading from pyjitpl's point of view self.metainterp.rebuild_state_after_failure(guard_op.getdescr(), "deadframe") assert len(self.metainterp.framestack) == 1 assert len(self.metainterp.virtualref_boxes) == 2 assert self.metainterp.virtualref_boxes[0].value == bxs1[0].value assert self.metainterp.virtualref_boxes[1].value == bxs2[0].value def test_make_vref_escape_after_finish(self): jitdriver = JitDriver(greens=[], reds=['n']) # class X: pass class ExCtx: pass exctx = ExCtx() # @dont_look_inside def g(vref): # we cannot do anything with the vref after the call to finish() pass # def f(n): while n > 0: jitdriver.can_enter_jit(n=n) jitdriver.jit_merge_point(n=n) x = X() x.n = n exctx.topframeref = vref = virtual_ref(x) # here, 'x' should be virtual exctx.topframeref = vref_None virtual_ref_finish(vref, x) # 'x' and 'vref' can randomly escape after the call to # finish(). g(vref) n -= 1 return 1 # self.meta_interp(f, [10]) self.check_resops(new_with_vtable=2) # the vref self.check_aborted_count(0) def test_simple_all_removed(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): 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 = vref = 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(vref, xy) # self.meta_interp(f, [15]) self.check_resops(new_with_vtable=0, new_array=0) self.check_aborted_count(0) 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 = vref = 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(vref, xy) # self.meta_interp(f, [15]) self.check_resops( new_with_vtable=2, # the vref: xy doesn't need to be forced new_array=0) # and neither xy.next1/2/3 self.check_aborted_count(0) 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 = vref = virtual_ref(xy) n -= externalfn(n) xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) virtual_ref_finish(vref, xy) exctx.topframeref = vref_None # self.meta_interp(f, [15]) self.check_resops( new_with_vtable=4, # XY(), the vref new_array=6) # next1/2/3 self.check_aborted_count(0) def test_simple_force_sometimes(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 == 13: exctx.m = exctx.topframeref().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) buf = lltype.malloc(rffi.CCHARP.TO, 1, flavor='raw') buf[0] = chr(n) # this is a raw virtual xy.next4 = lltype.malloc(rffi.CCHARP.TO, 1, flavor='raw') xy.n = n exctx.topframeref = vref = virtual_ref(xy) n -= externalfn(n) xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) lltype.free(xy.next4, flavor='raw') xy.next4 = lltype.nullptr(rffi.CCHARP.TO) virtual_ref_finish(vref, xy) exctx.topframeref = vref_None lltype.free(buf, flavor='raw') return exctx.m # res = self.meta_interp(f, [30]) assert res == 13 self.check_resops( new_with_vtable=2, # the vref, but not XY() new_array=0) # and neither next1/2/3 self.check_trace_count(1) self.check_aborted_count(0) def test_blackhole_forces(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): exctx.m = exctx.topframeref().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 = vref = virtual_ref(xy) if n == 13: externalfn(n) n -= 1 exctx.topframeref = vref_None xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) virtual_ref_finish(vref, xy) return exctx.m # res = self.meta_interp(f, [30]) assert res == 13 self.check_resops( new_with_vtable=0, # all virtualized in the n!=13 loop new_array=0) self.check_trace_count(1) self.check_aborted_count(0) def test_bridge_forces(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): exctx.m = exctx.topframeref().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.next4 = lltype.malloc(A, 0) xy.next5 = lltype.malloc(A, 0) xy.n = n exctx.topframeref = vref = virtual_ref(xy) if n % 6 == 0: xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) externalfn(n) n -= 1 exctx.topframeref = vref_None xy.next1 = lltype.nullptr(A) xy.next2 = lltype.nullptr(A) xy.next3 = lltype.nullptr(A) xy.next4 = lltype.nullptr(A) xy.next5 = lltype.nullptr(A) virtual_ref_finish(vref, xy) return exctx.m # res = self.meta_interp(f, [72]) assert res == 6 self.check_trace_count(2) # the loop and the bridge self.check_resops( new_with_vtable=2, # loop: nothing; bridge: vref, xy new_array=2) # bridge: next4, next5 self.check_aborted_count(0) def test_jit_force_virtual_seen(self): myjitdriver = JitDriver(greens=[], reds=['n']) A = lltype.GcArray(lltype.Signed) class XY(object): pass class ExCtx(object): pass exctx = ExCtx() escapes = [] def f(n): while n > 0: myjitdriver.can_enter_jit(n=n) myjitdriver.jit_merge_point(n=n) xy = XY() xy.n = n exctx.topframeref = vref = virtual_ref(xy) escapes.append(xy) xy.next1 = lltype.malloc(A, 0) n = exctx.topframeref().n - 1 exctx.topframeref = vref_None virtual_ref_finish(vref, xy) return 1 # res = self.meta_interp(f, [15]) assert res == 1 self.check_resops( new_with_vtable=2, # xy new_array=2) # next1 self.check_aborted_count(0) def test_recursive_call_1(self): myjitdriver = JitDriver(greens=[], reds=['n', 'rec', 'frame']) # class XY: pass class ExCtx: pass exctx = ExCtx() # def f(frame, n, reclevel): while n > 0: myjitdriver.can_enter_jit(n=n, frame=frame, rec=reclevel) myjitdriver.jit_merge_point(n=n, frame=frame, rec=reclevel) if reclevel == 0: return n xy = XY() exctx.topframeref = vref = virtual_ref(xy) m = f(xy, n, reclevel - 1) assert m == n n -= 1 exctx.topframeref = vref_None virtual_ref_finish(vref, xy) return 2 def main(n, reclevel): return f(XY(), n, reclevel) # res = self.meta_interp(main, [15, 1]) assert res == main(15, 1) self.check_aborted_count(0) def test_recursive_call_2(self): myjitdriver = JitDriver(greens=[], reds=['n', 'rec', 'frame']) # class XY: n = 0 class ExCtx: pass exctx = ExCtx() # def f(frame, n, reclevel): while n > 0: myjitdriver.can_enter_jit(n=n, frame=frame, rec=reclevel) myjitdriver.jit_merge_point(n=n, frame=frame, rec=reclevel) frame.n += 1 xy = XY() xy.n = n exctx.topframeref = vref = virtual_ref(xy) if reclevel > 0: m = f(xy, frame.n, reclevel - 1) assert xy.n == m n -= 1 else: n -= 2 exctx.topframeref = vref_None virtual_ref_finish(vref, xy) return frame.n def main(n, reclevel): return f(XY(), n, reclevel) # res = self.meta_interp(main, [10, 2]) assert res == main(10, 2) self.check_aborted_count(0) def test_alloc_virtualref_and_then_alloc_structure(self): myjitdriver = JitDriver(greens=[], reds=['n']) # class XY: pass class ExCtx: pass exctx = ExCtx() @dont_look_inside def escapexy(xy): print 'escapexy:', xy.n if xy.n % 5 == 0: vr = exctx.vr print 'accessing via vr:', vr() assert vr() is xy # def f(n): while n > 0: myjitdriver.jit_merge_point(n=n) xy = XY() xy.n = n vr = virtual_ref(xy) # force the virtualref to be allocated exctx.vr = vr # force xy to be allocated escapexy(xy) # clean up exctx.vr = vref_None virtual_ref_finish(vr, xy) n -= 1 return 1 # res = self.meta_interp(f, [15]) assert res == 1 self.check_resops(new_with_vtable=4) # vref, xy 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(UnknownException, "self.meta_interp(fn, [10])") def test_call_virtualref_already_forced(self): myjitdriver = JitDriver(greens=[], reds=['n', 'res']) # class XY: n = 0 # @dont_look_inside def force_it(vref, n): if n % 6 == 0: return vref().n return 0 def fn(n): res = 0 while n > 0: myjitdriver.can_enter_jit(n=n, res=res) myjitdriver.jit_merge_point(n=n, res=res) xy = XY() xy.n = n vref = virtual_ref(xy) force_it(vref, n) virtual_ref_finish(vref, xy) res += force_it( vref, n) # doesn't raise, because it was already forced n -= 1 return res # assert fn(10) == 6 res = self.meta_interp(fn, [10]) assert res == 6 def test_is_virtual(self): myjitdriver = JitDriver(greens=[], reds=['n', 'res1']) class X: pass @dont_look_inside def residual(vref): return vref.virtual # def f(n): res1 = -42 while n > 0: myjitdriver.jit_merge_point(n=n, res1=res1) x = X() vref = virtual_ref(x) res1 = residual(vref) virtual_ref_finish(vref, x) n -= 1 return res1 # res = self.meta_interp(f, [10]) assert res == 1 def test_is_not_virtual_none(self): myjitdriver = JitDriver(greens=[], reds=['n', 'res1']) @dont_look_inside def residual(vref): return vref.virtual # def f(n): res1 = -42 while n > 0: myjitdriver.jit_merge_point(n=n, res1=res1) res1 = residual(vref_None) n -= 1 return res1 # res = self.meta_interp(f, [10]) assert res == 0 def test_is_not_virtual_non_none(self): myjitdriver = JitDriver(greens=[], reds=['n', 'res1']) class X: pass @dont_look_inside def residual(vref): return vref.virtual # def f(n): res1 = -42 while n > 0: myjitdriver.jit_merge_point(n=n, res1=res1) x = X() res1 = residual(non_virtual_ref(x)) n -= 1 return res1 # res = self.meta_interp(f, [10]) assert res == 0 def test_force_virtual_vref(self): myjitdriver = JitDriver(greens=[], reds=['n', 'ec']) class ExecutionContext(object): pass class Frame(object): def __init__(self, x): self.x = x def f(n): ec = ExecutionContext() while n > 0: myjitdriver.jit_merge_point(n=n, ec=ec) frame = Frame(1) ec.topframeref = virtual_ref(frame) n -= ec.topframeref().x frame_vref = ec.topframeref ec.topframeref = vref_None virtual_ref_finish(frame_vref, frame) return n res = self.meta_interp(f, [10]) assert res == 0 self.check_resops({ 'int_sub': 2, 'int_gt': 2, 'jump': 1, 'guard_true': 2, 'force_token': 2, 'setfield_gc': 1 }) def test_vref_like_pypy(self): myjitdriver = JitDriver(greens=['n'], reds=['i', 'ec', 'frame']) class ExecutionContext(object): topframeref = vref_None def enter(self, frame): frame.f_backref = self.topframeref self.topframeref = virtual_ref(frame) def leave(self, frame): frame_vref = self.topframeref self.topframeref = frame.f_backref frame.f_backref() virtual_ref_finish(frame_vref, frame) class PyFrame(object): pass def dispatch(ec, frame, n): i = 0 while True: myjitdriver.jit_merge_point(n=n, ec=ec, frame=frame, i=i) i += 1 if n == 1: execute_frame(ec, 2) if i >= 10: break elif n == 2: execute_frame(ec, 3) if i == 2: break elif n == 3: break def execute_frame(ec, n): frame = PyFrame() ec.enter(frame) dispatch(ec, frame, n) ec.leave(frame) return n def entry_point(): return execute_frame(ExecutionContext(), 1) assert entry_point() == 1 r = self.meta_interp(entry_point, [], inline=True) assert r == 1
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.LLGraphCPU(None) NODE = lltype.GcForwardReference() NODE.become(lltype.GcStruct('NODE', ('parent', OBJECT), ('value', lltype.Signed), ('floatval', lltype.Float), ('charval', lltype.Char), ('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') chardescr = cpu.fielddescrof(NODE, 'charval') 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)) quasiptr = quasibox.value 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)) raw_malloc_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], [], [], EffectInfo.EF_CAN_RAISE, oopspecindex=EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR)) raw_free_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=EffectInfo.OS_RAW_FREE)) chararray = lltype.GcArray(lltype.Char) chararraydescr = cpu.arraydescrof(chararray) u2array = lltype.GcArray(rffi.USHORT) u2arraydescr = cpu.arraydescrof(u2array) # 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") complexarraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [complexarraydescr], [], [], [complexarraydescr], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=EffectInfo.OS_ARRAYCOPY)) rawarraydescr = cpu.arraydescrof(lltype.Array(lltype.Signed, hints={'nolength': True})) rawarraydescr_char = cpu.arraydescrof(lltype.Array(lltype.Char, hints={'nolength': True})) rawarraydescr_float = cpu.arraydescrof(lltype.Array(lltype.Float, hints={'nolength': True})) fc_array = lltype.GcArray( lltype.Struct( "floatchar", ("float", lltype.Float), ("char", lltype.Char))) fc_array_descr = cpu.arraydescrof(fc_array) fc_array_floatdescr = cpu.interiorfielddescrof(fc_array, "float") fc_array_chardescr = cpu.interiorfielddescrof(fc_array, "char") 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 rpython.jit.metainterp.virtualref import VirtualRefInfo class FakeWarmRunnerDesc: pass FakeWarmRunnerDesc.cpu = cpu vrefinfo = VirtualRefInfo(FakeWarmRunnerDesc) virtualtokendescr = vrefinfo.descr_virtual_token virtualforceddescr = vrefinfo.descr_forced FUNC = lltype.FuncType([], lltype.Void) ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, can_invalidate=False, oopspecindex=EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE) clear_vable = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, ei) 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()