def test_get_current_qmut_instance(): accessor = FieldListAccessor() accessor.initialize(None, {'inst_x': IR_QUASIIMMUTABLE}) STRUCT = lltype.GcStruct('Foo', ('inst_x', lltype.Signed), ('mutate_x', rclass.OBJECTPTR), hints={'immutable_fields': accessor}) foo = lltype.malloc(STRUCT, zero=True) foo.inst_x = 42 assert not foo.mutate_x class FakeCPU: ts = typesystem.llhelper def bh_getfield_gc_r(self, gcref, fielddescr): assert fielddescr == mutatefielddescr foo = lltype.cast_opaque_ptr(lltype.Ptr(STRUCT), gcref) result = foo.mutate_x return lltype.cast_opaque_ptr(llmemory.GCREF, result) def bh_setfield_gc_r(self, gcref, fielddescr, newvalue_gcref): assert fielddescr == mutatefielddescr foo = lltype.cast_opaque_ptr(lltype.Ptr(STRUCT), gcref) newvalue = lltype.cast_opaque_ptr(rclass.OBJECTPTR, newvalue_gcref) foo.mutate_x = newvalue cpu = FakeCPU() mutatefielddescr = ('fielddescr', STRUCT, 'mutate_x') foo_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, foo) qmut1 = get_current_qmut_instance(cpu, foo_gcref, mutatefielddescr) assert isinstance(qmut1, QuasiImmut) qmut2 = get_current_qmut_instance(cpu, foo_gcref, mutatefielddescr) assert qmut1 is qmut2
def test_quasi_immutable_setfield(): from pypy.rpython.rclass import FieldListAccessor, IR_QUASIIMMUTABLE accessor = FieldListAccessor() accessor.initialize(None, {'inst_x': IR_QUASIIMMUTABLE}) v1 = varoftype(lltype.Signed) STRUCT = lltype.GcStruct('struct', ('inst_x', lltype.Signed), ('mutate_x', rclass.OBJECTPTR), hints={'immutable_fields': accessor}) for v_x in [const(lltype.malloc(STRUCT)), varoftype(lltype.Ptr(STRUCT))]: op = SpaceOperation('jit_force_quasi_immutable', [v_x, Constant('mutate_x', lltype.Void)], varoftype(lltype.Void)) tr = Transformer(FakeCPU(), FakeRegularCallControl()) tr.graph = 'currentgraph' op0, op1 = tr.rewrite_operation(op) assert op0.opname == '-live-' assert op1.opname == 'jit_force_quasi_immutable' assert op1.args[0] == v_x assert op1.args[1] == ('fielddescr', STRUCT, 'mutate_x')
def __init__(self, rtyper, classdef): self._super().__init__(rtyper, classdef) classdesc = classdef.classdesc if '_virtualizable2_' in classdesc.classdict: basedesc = classdesc.basedesc assert basedesc is None or basedesc.lookup( '_virtualizable2_') is None self.top_of_virtualizable_hierarchy = True self.accessor = FieldListAccessor() else: self.top_of_virtualizable_hierarchy = False
def test_quasi_immutable_setfield(): from pypy.rpython.rclass import FieldListAccessor, IR_QUASIIMMUTABLE accessor = FieldListAccessor() accessor.initialize(None, {"inst_x": IR_QUASIIMMUTABLE}) v1 = varoftype(lltype.Signed) STRUCT = lltype.GcStruct( "struct", ("inst_x", lltype.Signed), ("mutate_x", rclass.OBJECTPTR), hints={"immutable_fields": accessor} ) for v_x in [const(lltype.malloc(STRUCT)), varoftype(lltype.Ptr(STRUCT))]: op = SpaceOperation( "jit_force_quasi_immutable", [v_x, Constant("mutate_x", lltype.Void)], varoftype(lltype.Void) ) tr = Transformer(FakeCPU(), FakeRegularCallControl()) tr.graph = "currentgraph" op0, op1 = tr.rewrite_operation(op) assert op0.opname == "-live-" assert op1.opname == "jit_force_quasi_immutable" assert op1.args[0] == v_x assert op1.args[1] == ("fielddescr", STRUCT, "mutate_x")
def test_quasi_immutable(): from pypy.rpython.rclass import FieldListAccessor, IR_QUASIIMMUTABLE accessor = FieldListAccessor() accessor.initialize(None, {'inst_x': IR_QUASIIMMUTABLE}) v2 = varoftype(lltype.Signed) STRUCT = lltype.GcStruct('struct', ('inst_x', lltype.Signed), ('mutate_x', rclass.OBJECTPTR), hints={'immutable_fields': accessor}) for v_x in [const(lltype.malloc(STRUCT)), varoftype(lltype.Ptr(STRUCT))]: op = SpaceOperation('getfield', [v_x, Constant('inst_x', lltype.Void)], v2) tr = Transformer(FakeCPU()) [_, op1, op2] = tr.rewrite_operation(op) assert op1.opname == 'record_quasiimmut_field' assert len(op1.args) == 3 assert op1.args[0] == v_x assert op1.args[1] == ('fielddescr', STRUCT, 'inst_x') assert op1.args[2] == ('fielddescr', STRUCT, 'mutate_x') assert op1.result is None assert op2.opname == 'getfield_gc_i' assert len(op2.args) == 2 assert op2.args[0] == v_x assert op2.args[1] == ('fielddescr', STRUCT, 'inst_x') assert op2.result is op.result
class LLtypeMixin(object): type_system = 'lltype' def get_class_of_box(self, box): return box.getref(rclass.OBJECTPTR).typeptr node_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) node_vtable.name = rclass.alloc_array_name('node') node_vtable_adr = llmemory.cast_ptr_to_adr(node_vtable) node_vtable2 = lltype.malloc(OBJECT_VTABLE, immortal=True) node_vtable2.name = rclass.alloc_array_name('node2') node_vtable_adr2 = llmemory.cast_ptr_to_adr(node_vtable2) cpu = runner.LLtypeCPU(None) NODE = lltype.GcForwardReference() NODE.become(lltype.GcStruct('NODE', ('parent', OBJECT), ('value', lltype.Signed), ('floatval', lltype.Float), ('next', lltype.Ptr(NODE)))) NODE2 = lltype.GcStruct('NODE2', ('parent', NODE), ('other', lltype.Ptr(NODE))) node = lltype.malloc(NODE) node.parent.typeptr = node_vtable node2 = lltype.malloc(NODE2) node2.parent.parent.typeptr = node_vtable2 nodebox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node)) myptr = nodebox.value myptr2 = lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(NODE)) nullptr = lltype.nullptr(llmemory.GCREF.TO) nodebox2 = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node2)) nodesize = cpu.sizeof(NODE) nodesize2 = cpu.sizeof(NODE2) valuedescr = cpu.fielddescrof(NODE, 'value') floatdescr = cpu.fielddescrof(NODE, 'floatval') nextdescr = cpu.fielddescrof(NODE, 'next') otherdescr = cpu.fielddescrof(NODE2, 'other') accessor = FieldListAccessor() accessor.initialize(None, {'inst_field': IR_QUASIIMMUTABLE}) QUASI = lltype.GcStruct('QUASIIMMUT', ('inst_field', lltype.Signed), ('mutate_field', rclass.OBJECTPTR), hints={'immutable_fields': accessor}) quasisize = cpu.sizeof(QUASI) quasi = lltype.malloc(QUASI, immortal=True) quasi.inst_field = -4247 quasifielddescr = cpu.fielddescrof(QUASI, 'inst_field') quasibox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, quasi)) quasiimmutdescr = QuasiImmutDescr(cpu, quasibox, quasifielddescr, cpu.fielddescrof(QUASI, 'mutate_field')) NODEOBJ = lltype.GcStruct('NODEOBJ', ('parent', OBJECT), ('ref', lltype.Ptr(OBJECT))) nodeobj = lltype.malloc(NODEOBJ) nodeobjvalue = lltype.cast_opaque_ptr(llmemory.GCREF, nodeobj) refdescr = cpu.fielddescrof(NODEOBJ, 'ref') INTOBJ_NOIMMUT = lltype.GcStruct('INTOBJ_NOIMMUT', ('parent', OBJECT), ('intval', lltype.Signed)) INTOBJ_IMMUT = lltype.GcStruct('INTOBJ_IMMUT', ('parent', OBJECT), ('intval', lltype.Signed), hints={'immutable': True}) intobj_noimmut_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) intobj_immut_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) noimmut_intval = cpu.fielddescrof(INTOBJ_NOIMMUT, 'intval') immut_intval = cpu.fielddescrof(INTOBJ_IMMUT, 'intval') PTROBJ_IMMUT = lltype.GcStruct('PTROBJ_IMMUT', ('parent', OBJECT), ('ptrval', lltype.Ptr(OBJECT)), hints={'immutable': True}) ptrobj_immut_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) immut_ptrval = cpu.fielddescrof(PTROBJ_IMMUT, 'ptrval') arraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Signed)) floatarraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Float)) # a GcStruct not inheriting from OBJECT S = lltype.GcStruct('TUPLE', ('a', lltype.Signed), ('b', lltype.Ptr(NODE))) ssize = cpu.sizeof(S) adescr = cpu.fielddescrof(S, 'a') bdescr = cpu.fielddescrof(S, 'b') sbox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S))) arraydescr2 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(S))) T = lltype.GcStruct('TUPLE', ('c', lltype.Signed), ('d', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE))))) tsize = cpu.sizeof(T) cdescr = cpu.fielddescrof(T, 'c') ddescr = cpu.fielddescrof(T, 'd') arraydescr3 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(NODE))) U = lltype.GcStruct('U', ('parent', OBJECT), ('one', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE))))) u_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) u_vtable_adr = llmemory.cast_ptr_to_adr(u_vtable) usize = cpu.sizeof(U) onedescr = cpu.fielddescrof(U, 'one') FUNC = lltype.FuncType([lltype.Signed], lltype.Signed) plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo.MOST_GENERAL) nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [])) writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [adescr], [])) writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [adescr], [arraydescr])) readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([adescr], [], [], [])) mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([nextdescr], [], [], [], EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE, can_invalidate=True)) arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [arraydescr], [], [arraydescr], EffectInfo.EF_CANNOT_RAISE, oopspecindex=EffectInfo.OS_ARRAYCOPY)) # array of structs (complex data) complexarray = lltype.GcArray( lltype.Struct("complex", ("real", lltype.Float), ("imag", lltype.Float), ) ) complexarraydescr = cpu.arraydescrof(complexarray) complexrealdescr = cpu.interiorfielddescrof(complexarray, "real") compleximagdescr = cpu.interiorfielddescrof(complexarray, "imag") for _name, _os in [ ('strconcatdescr', 'OS_STR_CONCAT'), ('strslicedescr', 'OS_STR_SLICE'), ('strequaldescr', 'OS_STR_EQUAL'), ('streq_slice_checknull_descr', 'OS_STREQ_SLICE_CHECKNULL'), ('streq_slice_nonnull_descr', 'OS_STREQ_SLICE_NONNULL'), ('streq_slice_char_descr', 'OS_STREQ_SLICE_CHAR'), ('streq_nonnull_descr', 'OS_STREQ_NONNULL'), ('streq_nonnull_char_descr', 'OS_STREQ_NONNULL_CHAR'), ('streq_checknull_char_descr', 'OS_STREQ_CHECKNULL_CHAR'), ('streq_lengthok_descr', 'OS_STREQ_LENGTHOK'), ]: _oopspecindex = getattr(EffectInfo, _os) locals()[_name] = \ cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=_oopspecindex)) # _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI')) locals()[_name.replace('str', 'unicode')] = \ cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=_oopspecindex)) s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE)) # class LoopToken(AbstractDescr): pass asmdescr = LoopToken() # it can be whatever, it's not a descr though from pypy.jit.metainterp.virtualref import VirtualRefInfo class FakeWarmRunnerDesc: pass FakeWarmRunnerDesc.cpu = cpu vrefinfo = VirtualRefInfo(FakeWarmRunnerDesc) virtualtokendescr = vrefinfo.descr_virtual_token virtualforceddescr = vrefinfo.descr_forced jit_virtual_ref_vtable = vrefinfo.jit_virtual_ref_vtable jvr_vtable_adr = llmemory.cast_ptr_to_adr(jit_virtual_ref_vtable) register_known_gctype(cpu, node_vtable, NODE) register_known_gctype(cpu, node_vtable2, NODE2) register_known_gctype(cpu, u_vtable, U) register_known_gctype(cpu, jit_virtual_ref_vtable,vrefinfo.JIT_VIRTUAL_REF) register_known_gctype(cpu, intobj_noimmut_vtable, INTOBJ_NOIMMUT) register_known_gctype(cpu, intobj_immut_vtable, INTOBJ_IMMUT) register_known_gctype(cpu, ptrobj_immut_vtable, PTROBJ_IMMUT) namespace = locals()
class ExplicitVirtualizableTests: XY = lltype.GcStruct( 'XY', ('parent', rclass.OBJECT), ('vable_token', lltype.Signed), ('inst_x', lltype.Signed), ('inst_node', lltype.Ptr(LLtypeMixin.NODE)), hints = {'virtualizable2_accessor': FieldListAccessor()}) XY._hints['virtualizable2_accessor'].initialize( XY, {'inst_x' : IR_IMMUTABLE, 'inst_node' : IR_IMMUTABLE}) xy_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True) heaptracker.set_testing_vtable_for_gcstruct(XY, xy_vtable, 'XY') def _freeze_(self): return True def setup(self): xy = lltype.malloc(self.XY) xy.vable_token = 0 xy.parent.typeptr = self.xy_vtable return xy def test_preexisting_access(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'], virtualizables = ['xy']) def f(n): xy = self.setup() xy.inst_x = 10 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n) myjitdriver.jit_merge_point(xy=xy, n=n) promote_virtualizable(xy, 'inst_x') x = xy.inst_x xy.inst_x = x + 1 n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x res = self.meta_interp(f, [20]) assert res == 30 self.check_simple_loop(setfield_gc=0, getfield_gc=0) def test_preexisting_access_2(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'], virtualizables = ['xy']) def f(n): xy = self.setup() xy.inst_x = 100 while n > -8: myjitdriver.can_enter_jit(xy=xy, n=n) myjitdriver.jit_merge_point(xy=xy, n=n) if n > 0: promote_virtualizable(xy, 'inst_x') x = xy.inst_x xy.inst_x = x + 1 else: promote_virtualizable(xy, 'inst_x') x = xy.inst_x xy.inst_x = x + 10 n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x assert f(5) == 185 res = self.meta_interp(f, [5]) assert res == 185 self.check_resops(setfield_gc=0, getfield_gc=2) # <= at the header of the loop def test_two_paths_access(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'], virtualizables = ['xy']) def f(n): xy = self.setup() xy.inst_x = 100 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n) myjitdriver.jit_merge_point(xy=xy, n=n) promote_virtualizable(xy, 'inst_x') x = xy.inst_x if n <= 10: x += 1000 promote_virtualizable(xy, 'inst_x') xy.inst_x = x + 1 n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x res = self.meta_interp(f, [18]) assert res == 10118 self.check_resops(setfield_gc=0, getfield_gc=2) def test_synchronize_in_return(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'], virtualizables = ['xy']) def g(xy, n): while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n) myjitdriver.jit_merge_point(xy=xy, n=n) promote_virtualizable(xy, 'inst_x') xy.inst_x += 1 n -= 1 def f(n): xy = self.setup() xy.inst_x = 10000 m = 10 while m > 0: g(xy, n) m -= 1 return xy.inst_x res = self.meta_interp(f, [18]) assert res == 10180 self.check_resops(setfield_gc=0, getfield_gc=2) def test_virtualizable_and_greens(self): myjitdriver = JitDriver(greens = ['m'], reds = ['n', 'xy'], virtualizables = ['xy']) def g(n): xy = self.setup() xy.inst_x = 10 m = 0 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n, m=m) myjitdriver.jit_merge_point(xy=xy, n=n, m=m) promote_virtualizable(xy, 'inst_x') x = xy.inst_x xy.inst_x = x + 1 m = (m+1) & 3 # the loop gets unrolled 4 times n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x def f(n): res = 0 k = 4 while k > 0: res += g(n) k -= 1 return res res = self.meta_interp(f, [40]) assert res == 50 * 4 self.check_resops(setfield_gc=0, getfield_gc=4) def test_double_frame(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy', 'other'], virtualizables = ['xy']) def f(n): xy = self.setup() xy.inst_x = 10 other = self.setup() other.inst_x = 15 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n, other=other) myjitdriver.jit_merge_point(xy=xy, n=n, other=other) promote_virtualizable(other, 'inst_x') value = other.inst_x # getfield_gc other.inst_x = value + 1 # setfield_gc promote_virtualizable(xy, 'inst_x') xy.inst_x = value + 100 # virtualized away n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x res = self.meta_interp(f, [20]) assert res == 134 self.check_simple_loop(setfield_gc=1, getfield_gc=0) self.check_resops(setfield_gc=2, getfield_gc=3) # ------------------------------ XY2 = lltype.GcStruct( 'XY2', ('parent', rclass.OBJECT), ('vable_token', lltype.Signed), ('inst_x', lltype.Signed), ('inst_l1', lltype.Ptr(lltype.GcArray(lltype.Signed))), ('inst_l2', lltype.Ptr(lltype.GcArray(lltype.Signed))), hints = {'virtualizable2_accessor': FieldListAccessor()}) XY2._hints['virtualizable2_accessor'].initialize( XY2, {'inst_x' : IR_IMMUTABLE, 'inst_l1' : IR_IMMUTABLE_ARRAY, 'inst_l2' : IR_IMMUTABLE_ARRAY}) xy2_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True) heaptracker.set_testing_vtable_for_gcstruct(XY2, xy2_vtable, 'XY2') def setup2(self): xy2 = lltype.malloc(self.XY2) xy2.vable_token = 0 xy2.parent.typeptr = self.xy2_vtable return xy2 def test_access_list_fields(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'], virtualizables = ['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def f(n): xy2 = self.setup2() xy2.inst_x = 100 xy2.inst_l1 = lltype.malloc(ARRAY, 3) xy2.inst_l1[0] = -9999999 xy2.inst_l1[1] = -9999999 xy2.inst_l1[2] = 3001 xy2.inst_l2 = lltype.malloc(ARRAY, 2) xy2.inst_l2[0] = 80 xy2.inst_l2[1] = -9999999 while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) promote_virtualizable(xy2, 'inst_l1') promote_virtualizable(xy2, 'inst_l2') xy2.inst_l1[2] += xy2.inst_l2[0] n -= 1 promote_virtualizable(xy2, 'inst_l1') return xy2.inst_l1[2] res = self.meta_interp(f, [16]) assert res == 3001 + 16 * 80 self.check_simple_loop(setarrayitem_gc=0, setfield_gc=0, getarrayitem_gc=0, getfield_gc=0) def test_synchronize_arrays_in_return(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'], virtualizables = ['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def g(xy2, n): while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) promote_virtualizable(xy2, 'inst_x') promote_virtualizable(xy2, 'inst_l2') xy2.inst_l2[0] += xy2.inst_x n -= 1 def f(n): xy2 = self.setup2() xy2.inst_x = 2 xy2.inst_l1 = lltype.malloc(ARRAY, 2) xy2.inst_l1[0] = 1941309 xy2.inst_l1[1] = 2941309 xy2.inst_l2 = lltype.malloc(ARRAY, 1) xy2.inst_l2[0] = 10000 m = 10 while m > 0: g(xy2, n) m -= 1 return xy2.inst_l2[0] assert f(18) == 10360 res = self.meta_interp(f, [18]) assert res == 10360 self.check_simple_loop(setfield_gc=0, getarrayitem_gc=0, getfield_gc=0, setarrayitem_gc=0) def test_array_length(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'], virtualizables = ['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def g(xy2, n): while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) promote_virtualizable(xy2, 'inst_l1') promote_virtualizable(xy2, 'inst_l2') xy2.inst_l1[1] += len(xy2.inst_l2) n -= 1 def f(n): xy2 = self.setup2() xy2.inst_x = 2 xy2.inst_l1 = lltype.malloc(ARRAY, 2) xy2.inst_l1[0] = 1941309 xy2.inst_l1[1] = 2941309 xy2.inst_l2 = lltype.malloc(ARRAY, 1) xy2.inst_l2[0] = 10000 g(xy2, n) return xy2.inst_l1[1] res = self.meta_interp(f, [18]) assert res == 2941309 + 18 self.check_simple_loop(setfield_gc=0, getarrayitem_gc=0, arraylen_gc=0, getfield_gc=0) def test_residual_function(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'], virtualizables = ['xy2']) ARRAY = lltype.GcArray(lltype.Signed) # @dont_look_inside def h(xy2): # this function is marked for residual calls because # it does something with a virtualizable's array that is not # just accessing an item return xy2.inst_l2 # def g(xy2, n): while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) promote_virtualizable(xy2, 'inst_l1') xy2.inst_l1[1] = xy2.inst_l1[1] + len(h(xy2)) n -= 1 def f(n): xy2 = self.setup2() xy2.inst_x = 2 xy2.inst_l1 = lltype.malloc(ARRAY, 2) xy2.inst_l1[0] = 1941309 xy2.inst_l1[1] = 2941309 xy2.inst_l2 = lltype.malloc(ARRAY, 1) xy2.inst_l2[0] = 10000 g(xy2, n) return xy2.inst_l1[1] res = self.meta_interp(f, [18]) assert res == 2941309 + 18 self.check_simple_loop(call=1, setfield_gc=0, getarrayitem_gc=0, arraylen_gc=1, getfield_gc=0) def test_double_frame_array(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2', 'other'], virtualizables = ['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def f(n): xy2 = self.setup2() xy2.inst_x = 10 xy2.inst_l1 = lltype.malloc(ARRAY, 1) xy2.inst_l1[0] = 1982731 xy2.inst_l2 = lltype.malloc(ARRAY, 1) xy2.inst_l2[0] = 10000 other = self.setup2() other.inst_x = 15 other.inst_l1 = lltype.malloc(ARRAY, 2) other.inst_l1[0] = 189182 other.inst_l1[1] = 58421 other.inst_l2 = lltype.malloc(ARRAY, 2) other.inst_l2[0] = 181 other.inst_l2[1] = 189 while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n, other=other) myjitdriver.jit_merge_point(xy2=xy2, n=n, other=other) promote_virtualizable(other, 'inst_l2') length = len(other.inst_l2) # getfield_gc/arraylen_gc value = other.inst_l2[0] # getfield_gc/getarrayitem_gc other.inst_l2[0] = value + length # getfield_gc/setarrayitem_gc promote_virtualizable(xy2, 'inst_l2') xy2.inst_l2[0] = value + 100 # virtualized away n -= 1 promote_virtualizable(xy2, 'inst_l2') return xy2.inst_l2[0] expected = f(20) res = self.meta_interp(f, [20], enable_opts='') assert res == expected self.check_simple_loop(setarrayitem_gc=1, setfield_gc=0, getarrayitem_gc=1, arraylen_gc=1, getfield_gc=1) # ------------------------------ XY2SUB = lltype.GcStruct( 'XY2SUB', ('parent', XY2)) xy2sub_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True) heaptracker.set_testing_vtable_for_gcstruct(XY2SUB, xy2sub_vtable, 'XY2SUB') def setup2sub(self): xy2 = lltype.malloc(self.XY2SUB) xy2.parent.vable_token = 0 xy2.parent.parent.typeptr = self.xy2_vtable return xy2 def test_subclass(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'], virtualizables = ['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def g(xy2, n): while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) parent = xy2.parent promote_virtualizable(parent, 'inst_x') promote_virtualizable(parent, 'inst_l2') parent.inst_l2[0] += parent.inst_x n -= 1 def f(n): xy2 = self.setup2sub() xy2.parent.inst_x = 2 xy2.parent.inst_l1 = lltype.malloc(ARRAY, 2) xy2.parent.inst_l1[0] = 1941309 xy2.parent.inst_l1[1] = 2941309 xy2.parent.inst_l2 = lltype.malloc(ARRAY, 1) xy2.parent.inst_l2[0] = 10000 m = 10 while m > 0: g(xy2, n) m -= 1 return xy2.parent.inst_l2[0] assert f(18) == 10360 res = self.meta_interp(f, [18]) assert res == 10360 self.check_simple_loop(getfield_gc=0, getarrayitem_gc=0, setfield_gc=0, setarrayitem_gc=0)
class ExplicitVirtualizableTests: XY = lltype.GcStruct( 'XY', ('parent', rclass.OBJECT), ('vable_base', llmemory.Address), ('vable_rti', VABLERTIPTR), ('inst_x', lltype.Signed), ('inst_node', lltype.Ptr(LLtypeMixin.NODE)), hints={'virtualizable2_accessor': FieldListAccessor()}) XY._hints['virtualizable2_accessor'].initialize(XY, { 'inst_x': "", 'inst_node': "" }) xy_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True) heaptracker.set_testing_vtable_for_gcstruct(XY, xy_vtable, 'XY') def _freeze_(self): return True def setup(self): xy = lltype.malloc(self.XY) xy.vable_rti = lltype.nullptr(VABLERTIPTR.TO) xy.parent.typeptr = self.xy_vtable return xy def test_preexisting_access(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy'], virtualizables=['xy']) def f(n): xy = self.setup() xy.inst_x = 10 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n) myjitdriver.jit_merge_point(xy=xy, n=n) promote_virtualizable(xy, 'inst_x') x = xy.inst_x xy.inst_x = x + 1 n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x res = self.meta_interp(f, [20]) assert res == 30 self.check_loops(getfield_gc=0, setfield_gc=0) def test_preexisting_access_2(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy'], virtualizables=['xy']) def f(n): xy = self.setup() xy.inst_x = 100 while n > -8: myjitdriver.can_enter_jit(xy=xy, n=n) myjitdriver.jit_merge_point(xy=xy, n=n) if n > 0: promote_virtualizable(xy, 'inst_x') x = xy.inst_x xy.inst_x = x + 1 else: promote_virtualizable(xy, 'inst_x') x = xy.inst_x xy.inst_x = x + 10 n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x assert f(5) == 185 res = self.meta_interp(f, [5]) assert res == 185 self.check_loops(getfield_gc=0, setfield_gc=0) def test_two_paths_access(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy'], virtualizables=['xy']) def f(n): xy = self.setup() xy.inst_x = 100 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n) myjitdriver.jit_merge_point(xy=xy, n=n) promote_virtualizable(xy, 'inst_x') x = xy.inst_x if n <= 10: x += 1000 promote_virtualizable(xy, 'inst_x') xy.inst_x = x + 1 n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x res = self.meta_interp(f, [18]) assert res == 10118 self.check_loops(getfield_gc=0, setfield_gc=0) def test_synchronize_in_return(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy'], virtualizables=['xy']) def g(xy, n): while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n) myjitdriver.jit_merge_point(xy=xy, n=n) promote_virtualizable(xy, 'inst_x') xy.inst_x += 1 n -= 1 def f(n): xy = self.setup() xy.inst_x = 10000 m = 10 while m > 0: g(xy, n) m -= 1 return xy.inst_x res = self.meta_interp(f, [18]) assert res == 10180 self.check_loops(getfield_gc=0, setfield_gc=0) def test_virtualizable_and_greens(self): myjitdriver = JitDriver(greens=['m'], reds=['n', 'xy'], virtualizables=['xy']) def g(n): xy = self.setup() xy.inst_x = 10 m = 0 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n, m=m) myjitdriver.jit_merge_point(xy=xy, n=n, m=m) promote_virtualizable(xy, 'inst_x') x = xy.inst_x xy.inst_x = x + 1 m = (m + 1) & 3 # the loop gets unrolled 4 times n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x def f(n): res = 0 k = 4 while k > 0: res += g(n) k -= 1 return res res = self.meta_interp(f, [40]) assert res == 50 * 4 self.check_loops(getfield_gc=0, setfield_gc=0) def test_double_frame(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy', 'other'], virtualizables=['xy']) def f(n): xy = self.setup() xy.inst_x = 10 other = self.setup() other.inst_x = 15 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n, other=other) myjitdriver.jit_merge_point(xy=xy, n=n, other=other) promote_virtualizable(other, 'inst_x') value = other.inst_x # getfield_gc other.inst_x = value + 1 # setfield_gc promote_virtualizable(xy, 'inst_x') xy.inst_x = value + 100 # virtualized away n -= 1 promote_virtualizable(xy, 'inst_x') return xy.inst_x res = self.meta_interp(f, [20]) assert res == 134 self.check_loops(getfield_gc=1, setfield_gc=1) def test_external_read_while_tracing(self): myjitdriver = JitDriver(greens=[], reds=['n', 'm', 'xy'], virtualizables=['xy']) class Outer: pass outer = Outer() def ext(): xy = outer.xy promote_virtualizable(xy, 'inst_x') return xy.inst_x + 2 def f(n): xy = self.setup() xy.inst_x = 10 outer.xy = xy m = 0 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n, m=m) myjitdriver.jit_merge_point(xy=xy, n=n, m=m) promote_virtualizable(xy, 'inst_x') xy.inst_x = n + 9998 # virtualized away m += ext() # 2x setfield_gc, 2x getfield_gc promote_virtualizable(xy, 'inst_x') xy.inst_x = 10 # virtualized away n -= 1 return m assert f(20) == 10000 * 20 + (20 * 21) / 2 res = self.meta_interp(f, [20], policy=StopAtXPolicy(ext)) assert res == 10000 * 20 + (20 * 21) / 2 self.check_loops(call=1, getfield_gc=2, setfield_gc=2) # xxx for now a call that forces the virtualizable during tracing # is supposed to always force it later too. def test_external_write_while_tracing(self): myjitdriver = JitDriver(greens=[], reds=['n', 'm', 'xy'], virtualizables=['xy']) class Outer: pass outer = Outer() def ext(): xy = outer.xy promote_virtualizable(xy, 'inst_x') xy.inst_x += 2 def f(n): xy = self.setup() xy.inst_x = 10 outer.xy = xy m = 0 while n > 0: myjitdriver.can_enter_jit(xy=xy, n=n, m=m) myjitdriver.jit_merge_point(xy=xy, n=n, m=m) promote_virtualizable(xy, 'inst_x') xy.inst_x = n + 9998 # virtualized away ext() # 2x setfield_gc, 2x getfield_gc promote_virtualizable(xy, 'inst_x') m += xy.inst_x # virtualized away n -= 1 return m res = self.meta_interp(f, [20], policy=StopAtXPolicy(ext)) assert res == f(20) self.check_loops(call=1, getfield_gc=2, setfield_gc=2) # xxx for now a call that forces the virtualizable during tracing # is supposed to always force it later too. # ------------------------------ XY2 = lltype.GcStruct( 'XY2', ('parent', rclass.OBJECT), ('vable_base', llmemory.Address), ('vable_rti', VABLERTIPTR), ('inst_x', lltype.Signed), ('inst_l1', lltype.Ptr(lltype.GcArray(lltype.Signed))), ('inst_l2', lltype.Ptr(lltype.GcArray(lltype.Signed))), hints={'virtualizable2_accessor': FieldListAccessor()}) XY2._hints['virtualizable2_accessor'].initialize(XY2, { 'inst_x': "", 'inst_l1': "[*]", 'inst_l2': "[*]" }) xy2_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True) heaptracker.set_testing_vtable_for_gcstruct(XY2, xy2_vtable, 'XY2') def setup2(self): xy2 = lltype.malloc(self.XY2) xy2.vable_rti = lltype.nullptr(VABLERTIPTR.TO) xy2.parent.typeptr = self.xy2_vtable return xy2 def test_access_list_fields(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy2'], virtualizables=['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def f(n): xy2 = self.setup2() xy2.inst_x = 100 xy2.inst_l1 = lltype.malloc(ARRAY, 3) xy2.inst_l1[0] = -9999999 xy2.inst_l1[1] = -9999999 xy2.inst_l1[2] = 3001 xy2.inst_l2 = lltype.malloc(ARRAY, 2) xy2.inst_l2[0] = 80 xy2.inst_l2[1] = -9999999 while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) promote_virtualizable(xy2, 'inst_l1') promote_virtualizable(xy2, 'inst_l2') xy2.inst_l1[2] += xy2.inst_l2[0] n -= 1 promote_virtualizable(xy2, 'inst_l1') return xy2.inst_l1[2] res = self.meta_interp(f, [16]) assert res == 3001 + 16 * 80 self.check_loops(getfield_gc=0, setfield_gc=0, getarrayitem_gc=0, setarrayitem_gc=0) def test_synchronize_arrays_in_return(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy2'], virtualizables=['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def g(xy2, n): while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) promote_virtualizable(xy2, 'inst_x') promote_virtualizable(xy2, 'inst_l2') xy2.inst_l2[0] += xy2.inst_x n -= 1 def f(n): xy2 = self.setup2() xy2.inst_x = 2 xy2.inst_l1 = lltype.malloc(ARRAY, 2) xy2.inst_l1[0] = 1941309 xy2.inst_l1[1] = 2941309 xy2.inst_l2 = lltype.malloc(ARRAY, 1) xy2.inst_l2[0] = 10000 m = 10 while m > 0: g(xy2, n) m -= 1 return xy2.inst_l2[0] assert f(18) == 10360 res = self.meta_interp(f, [18]) assert res == 10360 self.check_loops(getfield_gc=0, setfield_gc=0, getarrayitem_gc=0) def test_array_length(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy2'], virtualizables=['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def g(xy2, n): while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) promote_virtualizable(xy2, 'inst_l1') promote_virtualizable(xy2, 'inst_l2') xy2.inst_l1[1] += len(xy2.inst_l2) n -= 1 def f(n): xy2 = self.setup2() xy2.inst_x = 2 xy2.inst_l1 = lltype.malloc(ARRAY, 2) xy2.inst_l1[0] = 1941309 xy2.inst_l1[1] = 2941309 xy2.inst_l2 = lltype.malloc(ARRAY, 1) xy2.inst_l2[0] = 10000 g(xy2, n) return xy2.inst_l1[1] res = self.meta_interp(f, [18]) assert res == 2941309 + 18 self.check_loops(getfield_gc=0, setfield_gc=0, getarrayitem_gc=0, arraylen_gc=0) def test_residual_function(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy2'], virtualizables=['xy2']) ARRAY = lltype.GcArray(lltype.Signed) # @dont_look_inside def h(xy2): # this function is marked for residual calls because # it does something with a virtualizable's array that is not # just accessing an item return xy2.inst_l2 # def g(xy2, n): while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) promote_virtualizable(xy2, 'inst_l1') xy2.inst_l1[1] = xy2.inst_l1[1] + len(h(xy2)) n -= 1 def f(n): xy2 = self.setup2() xy2.inst_x = 2 xy2.inst_l1 = lltype.malloc(ARRAY, 2) xy2.inst_l1[0] = 1941309 xy2.inst_l1[1] = 2941309 xy2.inst_l2 = lltype.malloc(ARRAY, 1) xy2.inst_l2[0] = 10000 g(xy2, n) return xy2.inst_l1[1] res = self.meta_interp(f, [18]) assert res == 2941309 + 18 self.check_loops(getfield_gc=0, setfield_gc=0, getarrayitem_gc=0, arraylen_gc=1, call=1) def test_double_frame_array(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy2', 'other'], virtualizables=['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def f(n): xy2 = self.setup2() xy2.inst_x = 10 xy2.inst_l1 = lltype.malloc(ARRAY, 1) xy2.inst_l1[0] = 1982731 xy2.inst_l2 = lltype.malloc(ARRAY, 1) xy2.inst_l2[0] = 10000 other = self.setup2() other.inst_x = 15 other.inst_l1 = lltype.malloc(ARRAY, 2) other.inst_l1[0] = 189182 other.inst_l1[1] = 58421 other.inst_l2 = lltype.malloc(ARRAY, 2) other.inst_l2[0] = 181 other.inst_l2[1] = 189 while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n, other=other) myjitdriver.jit_merge_point(xy2=xy2, n=n, other=other) promote_virtualizable(other, 'inst_l2') length = len(other.inst_l2) # getfield_gc/arraylen_gc value = other.inst_l2[0] # getfield_gc/getarrayitem_gc other.inst_l2[ 0] = value + length # getfield_gc/setarrayitem_gc promote_virtualizable(xy2, 'inst_l2') xy2.inst_l2[0] = value + 100 # virtualized away n -= 1 promote_virtualizable(xy2, 'inst_l2') return xy2.inst_l2[0] expected = f(20) res = self.meta_interp(f, [20], optimizer=OPTIMIZER_SIMPLE) assert res == expected self.check_loops(getfield_gc=3, setfield_gc=0, arraylen_gc=1, getarrayitem_gc=1, setarrayitem_gc=1) # ------------------------------ XY2SUB = lltype.GcStruct('XY2SUB', ('parent', XY2)) xy2sub_vtable = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True) heaptracker.set_testing_vtable_for_gcstruct(XY2SUB, xy2sub_vtable, 'XY2SUB') def setup2sub(self): xy2 = lltype.malloc(self.XY2SUB) xy2.parent.vable_rti = lltype.nullptr(VABLERTIPTR.TO) xy2.parent.parent.typeptr = self.xy2_vtable return xy2 def test_subclass(self): myjitdriver = JitDriver(greens=[], reds=['n', 'xy2'], virtualizables=['xy2']) ARRAY = lltype.GcArray(lltype.Signed) def g(xy2, n): while n > 0: myjitdriver.can_enter_jit(xy2=xy2, n=n) myjitdriver.jit_merge_point(xy2=xy2, n=n) parent = xy2.parent promote_virtualizable(parent, 'inst_x') promote_virtualizable(parent, 'inst_l2') parent.inst_l2[0] += parent.inst_x n -= 1 def f(n): xy2 = self.setup2sub() xy2.parent.inst_x = 2 xy2.parent.inst_l1 = lltype.malloc(ARRAY, 2) xy2.parent.inst_l1[0] = 1941309 xy2.parent.inst_l1[1] = 2941309 xy2.parent.inst_l2 = lltype.malloc(ARRAY, 1) xy2.parent.inst_l2[0] = 10000 m = 10 while m > 0: g(xy2, n) m -= 1 return xy2.parent.inst_l2[0] assert f(18) == 10360 res = self.meta_interp(f, [18]) assert res == 10360 self.check_loops(getfield_gc=0, setfield_gc=0, getarrayitem_gc=0)