Author: Hakan Ardo <ha...@debian.org> Branch: Changeset: r56435:067825aa8f90 Date: 2012-07-24 19:06 +0200 http://bitbucket.org/pypy/pypy/changeset/067825aa8f90/
Log: Merge jit-opaque-licm. It allows the heap optimizer to cache getitems of opaque pointers across loop boundaries when their class is known. diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py --- a/pypy/jit/metainterp/optimizeopt/heap.py +++ b/pypy/jit/metainterp/optimizeopt/heap.py @@ -1,7 +1,7 @@ import os from pypy.jit.metainterp.jitexc import JitException -from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, MODE_ARRAY +from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, MODE_ARRAY, LEVEL_KNOWNCLASS from pypy.jit.metainterp.history import ConstInt, Const from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method from pypy.jit.metainterp.resoperation import rop, ResOperation @@ -128,8 +128,12 @@ op = self._cached_fields_getfield_op[structvalue] if not op: continue - if optimizer.getvalue(op.getarg(0)) in optimizer.opaque_pointers: - continue + value = optimizer.getvalue(op.getarg(0)) + if value in optimizer.opaque_pointers: + if value.level < LEVEL_KNOWNCLASS: + continue + if op.getopnum() != rop.SETFIELD_GC and op.getopnum() != rop.GETFIELD_GC: + continue if structvalue in self._cached_fields: if op.getopnum() == rop.SETFIELD_GC: result = op.getarg(1) diff --git a/pypy/jit/metainterp/optimizeopt/test/test_multilabel.py b/pypy/jit/metainterp/optimizeopt/test/test_multilabel.py --- a/pypy/jit/metainterp/optimizeopt/test/test_multilabel.py +++ b/pypy/jit/metainterp/optimizeopt/test/test_multilabel.py @@ -431,7 +431,53 @@ jump(i55, i81) """ self.optimize_loop(ops, expected) - + + def test_boxed_opaque_unknown_class(self): + ops = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + mark_opaque_ptr(p2) + i3 = getfield_gc(p2, descr=otherdescr) + label(p1) + i4 = getfield_gc(p1, descr=otherdescr) + label(p1) + p5 = getfield_gc(p1, descr=nextdescr) + mark_opaque_ptr(p5) + i6 = getfield_gc(p5, descr=otherdescr) + i7 = call(i6, descr=nonwritedescr) + """ + expected = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + i3 = getfield_gc(p2, descr=otherdescr) + label(p1) + i4 = getfield_gc(p1, descr=otherdescr) + label(p1) + p5 = getfield_gc(p1, descr=nextdescr) + i6 = getfield_gc(p5, descr=otherdescr) + i7 = call(i6, descr=nonwritedescr) + """ + self.optimize_loop(ops, expected) + + def test_opaque_pointer_fails_to_close_loop(self): + ops = """ + [p1, p11] + p2 = getfield_gc(p1, descr=nextdescr) + guard_class(p2, ConstClass(node_vtable)) [] + mark_opaque_ptr(p2) + i3 = getfield_gc(p2, descr=otherdescr) + label(p1, p11) + p12 = getfield_gc(p1, descr=nextdescr) + i13 = getfield_gc(p2, descr=otherdescr) + i14 = call(i13, descr=nonwritedescr) + jump(p11, p1) + """ + with raises(InvalidLoop): + self.optimize_loop(ops, ops) + + + + class OptRenameStrlen(Optimization): def propagate_forward(self, op): dispatch_opt(self, op) diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py --- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py +++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py @@ -7872,6 +7872,73 @@ self.raises(InvalidLoop, self.optimize_loop, ops, ops) + def test_licm_boxed_opaque_getitem(self): + ops = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + mark_opaque_ptr(p2) + guard_class(p2, ConstClass(node_vtable)) [] + i3 = getfield_gc(p2, descr=otherdescr) + i4 = call(i3, descr=nonwritedescr) + jump(p1) + """ + expected = """ + [p1, i3] + i4 = call(i3, descr=nonwritedescr) + jump(p1, i3) + """ + self.optimize_loop(ops, expected) + + def test_licm_boxed_opaque_getitem_unknown_class(self): + ops = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + mark_opaque_ptr(p2) + i3 = getfield_gc(p2, descr=otherdescr) + i4 = call(i3, descr=nonwritedescr) + jump(p1) + """ + expected = """ + [p1, p2] + i3 = getfield_gc(p2, descr=otherdescr) + i4 = call(i3, descr=nonwritedescr) + jump(p1, p2) + """ + self.optimize_loop(ops, expected) + + def test_licm_unboxed_opaque_getitem(self): + ops = """ + [p2] + mark_opaque_ptr(p2) + guard_class(p2, ConstClass(node_vtable)) [] + i3 = getfield_gc(p2, descr=otherdescr) + i4 = call(i3, descr=nonwritedescr) + jump(p2) + """ + expected = """ + [p1, i3] + i4 = call(i3, descr=nonwritedescr) + jump(p1, i3) + """ + self.optimize_loop(ops, expected) + + def test_licm_unboxed_opaque_getitem_unknown_class(self): + ops = """ + [p2] + mark_opaque_ptr(p2) + i3 = getfield_gc(p2, descr=otherdescr) + i4 = call(i3, descr=nonwritedescr) + jump(p2) + """ + expected = """ + [p2] + i3 = getfield_gc(p2, descr=otherdescr) + i4 = call(i3, descr=nonwritedescr) + jump(p2) + """ + self.optimize_loop(ops, expected) + + class TestLLtype(OptimizeOptTest, LLtypeMixin): pass diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py --- a/pypy/jit/metainterp/optimizeopt/unroll.py +++ b/pypy/jit/metainterp/optimizeopt/unroll.py @@ -341,6 +341,12 @@ op = self.short[i] newop = self.short_inliner.inline_op(op) self.optimizer.send_extra_operation(newop) + if op.result in self.short_boxes.assumed_classes: + classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu) + assumed_classbox = self.short_boxes.assumed_classes[op.result] + if not classbox or not classbox.same_constant(assumed_classbox): + raise InvalidLoop('Class of opaque pointer needed in short ' + + 'preamble unknown at end of loop') i += 1 # Import boxes produced in the preamble but used in the loop @@ -432,9 +438,13 @@ newargs[i] = a.clonebox() boxmap[a] = newargs[i] inliner = Inliner(short_inputargs, newargs) + target_token.assumed_classes = {} for i in range(len(short)): - short[i] = inliner.inline_op(short[i]) - + op = short[i] + newop = inliner.inline_op(op) + if op.result and op.result in self.short_boxes.assumed_classes: + target_token.assumed_classes[newop.result] = self.short_boxes.assumed_classes[op.result] + short[i] = newop target_token.resume_at_jump_descr = target_token.resume_at_jump_descr.clone_if_mutable() inliner.inline_descr_inplace(target_token.resume_at_jump_descr) @@ -588,6 +598,12 @@ for shop in target.short_preamble[1:]: newop = inliner.inline_op(shop) self.optimizer.send_extra_operation(newop) + if shop.result in target.assumed_classes: + classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu) + if not classbox or not classbox.same_constant(target.assumed_classes[shop.result]): + raise InvalidLoop('The class of an opaque pointer at the end ' + + 'of the bridge does not mach the class ' + + 'it has at the start of the target loop') except InvalidLoop: #debug_print("Inlining failed unexpectedly", # "jumping to preamble instead") diff --git a/pypy/jit/metainterp/optimizeopt/virtualstate.py b/pypy/jit/metainterp/optimizeopt/virtualstate.py --- a/pypy/jit/metainterp/optimizeopt/virtualstate.py +++ b/pypy/jit/metainterp/optimizeopt/virtualstate.py @@ -288,7 +288,8 @@ class NotVirtualStateInfo(AbstractVirtualStateInfo): - def __init__(self, value): + def __init__(self, value, is_opaque=False): + self.is_opaque = is_opaque self.known_class = value.known_class self.level = value.level if value.intbound is None: @@ -357,6 +358,9 @@ if self.lenbound or other.lenbound: raise InvalidLoop('The array length bounds does not match.') + if self.is_opaque: + raise InvalidLoop('Generating guards for opaque pointers is not safe') + if self.level == LEVEL_KNOWNCLASS and \ box.nonnull() and \ self.known_class.same_constant(cpu.ts.cls_of_box(box)): @@ -560,7 +564,8 @@ return VirtualState([self.state(box) for box in jump_args]) def make_not_virtual(self, value): - return NotVirtualStateInfo(value) + is_opaque = value in self.optimizer.opaque_pointers + return NotVirtualStateInfo(value, is_opaque) def make_virtual(self, known_class, fielddescrs): return VirtualStateInfo(known_class, fielddescrs) @@ -585,6 +590,7 @@ self.rename = {} self.optimizer = optimizer self.availible_boxes = availible_boxes + self.assumed_classes = {} if surviving_boxes is not None: for box in surviving_boxes: @@ -678,6 +684,12 @@ raise BoxNotProducable def add_potential(self, op, synthetic=False): + if op.result and op.result in self.optimizer.values: + value = self.optimizer.values[op.result] + if value in self.optimizer.opaque_pointers: + classbox = value.get_constant_class(self.optimizer.cpu) + if classbox: + self.assumed_classes[op.result] = classbox if op.result not in self.potential_ops: self.potential_ops[op.result] = op else: diff --git a/pypy/jit/metainterp/test/test_loop.py b/pypy/jit/metainterp/test/test_loop.py --- a/pypy/jit/metainterp/test/test_loop.py +++ b/pypy/jit/metainterp/test/test_loop.py @@ -871,6 +871,42 @@ res = self.meta_interp(f, [20, 10, 1]) assert res == f(20, 10, 1) + def test_boxed_unerased_pointers_in_short_preamble(self): + from pypy.rlib.rerased import new_erasing_pair + from pypy.rpython.lltypesystem import lltype + class A(object): + def __init__(self, val): + self.val = val + def tst(self): + return self.val + + class Box(object): + def __init__(self, val): + self.val = val + + erase_A, unerase_A = new_erasing_pair('A') + erase_TP, unerase_TP = new_erasing_pair('TP') + TP = lltype.GcArray(lltype.Signed) + myjitdriver = JitDriver(greens = [], reds = ['n', 'm', 'i', 'sa', 'p']) + def f(n, m): + i = sa = 0 + p = Box(erase_A(A(7))) + while i < n: + myjitdriver.jit_merge_point(n=n, m=m, i=i, sa=sa, p=p) + if i < m: + sa += unerase_A(p.val).tst() + elif i == m: + a = lltype.malloc(TP, 5) + a[0] = 42 + p = Box(erase_TP(a)) + else: + sa += unerase_TP(p.val)[0] + sa -= A(i).val + i += 1 + return sa + res = self.meta_interp(f, [20, 10]) + assert res == f(20, 10) + class TestOOtype(LoopTest, OOJitMixin): pass diff --git a/pypy/jit/metainterp/test/test_virtualstate.py b/pypy/jit/metainterp/test/test_virtualstate.py --- a/pypy/jit/metainterp/test/test_virtualstate.py +++ b/pypy/jit/metainterp/test/test_virtualstate.py @@ -908,6 +908,141 @@ """ self.optimize_bridge(loop, bridge, expected, p5=self.myptr, p6=self.myptr2) + def test_licm_boxed_opaque_getitem(self): + loop = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + mark_opaque_ptr(p2) + guard_class(p2, ConstClass(node_vtable)) [] + i3 = getfield_gc(p2, descr=otherdescr) + i4 = call(i3, descr=nonwritedescr) + jump(p1) + """ + bridge = """ + [p1] + guard_nonnull(p1) [] + jump(p1) + """ + expected = """ + [p1] + guard_nonnull(p1) [] + p2 = getfield_gc(p1, descr=nextdescr) + jump(p1) + """ + self.optimize_bridge(loop, bridge, expected, 'Preamble') + + bridge = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + guard_class(p2, ConstClass(node_vtable2)) [] + jump(p1) + """ + expected = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + guard_class(p2, ConstClass(node_vtable2)) [] + jump(p1) + """ + self.optimize_bridge(loop, bridge, expected, 'Preamble') + + bridge = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + guard_class(p2, ConstClass(node_vtable)) [] + jump(p1) + """ + expected = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + guard_class(p2, ConstClass(node_vtable)) [] + i3 = getfield_gc(p2, descr=otherdescr) + jump(p1, i3) + """ + self.optimize_bridge(loop, bridge, expected, 'Loop') + + def test_licm_unboxed_opaque_getitem(self): + loop = """ + [p2] + mark_opaque_ptr(p2) + guard_class(p2, ConstClass(node_vtable)) [] + i3 = getfield_gc(p2, descr=otherdescr) + i4 = call(i3, descr=nonwritedescr) + jump(p2) + """ + bridge = """ + [p1] + guard_nonnull(p1) [] + jump(p1) + """ + self.optimize_bridge(loop, bridge, 'RETRACE', p1=self.myptr) + self.optimize_bridge(loop, bridge, 'RETRACE', p1=self.myptr2) + + bridge = """ + [p2] + guard_class(p2, ConstClass(node_vtable2)) [] + jump(p2) + """ + self.optimize_bridge(loop, bridge, 'RETRACE') + + bridge = """ + [p2] + guard_class(p2, ConstClass(node_vtable)) [] + jump(p2) + """ + expected = """ + [p2] + guard_class(p2, ConstClass(node_vtable)) [] + i3 = getfield_gc(p2, descr=otherdescr) + jump(p2, i3) + """ + self.optimize_bridge(loop, bridge, expected, 'Loop') + + def test_licm_virtual_opaque_getitem(self): + loop = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + mark_opaque_ptr(p2) + guard_class(p2, ConstClass(node_vtable)) [] + i3 = getfield_gc(p2, descr=otherdescr) + i4 = call(i3, descr=nonwritedescr) + p3 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p3, p2, descr=nextdescr) + jump(p3) + """ + bridge = """ + [p1] + p3 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p3, p1, descr=nextdescr) + jump(p3) + """ + self.optimize_bridge(loop, bridge, 'RETRACE', p1=self.myptr) + self.optimize_bridge(loop, bridge, 'RETRACE', p1=self.myptr2) + + bridge = """ + [p1] + p3 = new_with_vtable(ConstClass(node_vtable)) + guard_class(p1, ConstClass(node_vtable2)) [] + setfield_gc(p3, p1, descr=nextdescr) + jump(p3) + """ + self.optimize_bridge(loop, bridge, 'RETRACE') + + bridge = """ + [p1] + p3 = new_with_vtable(ConstClass(node_vtable)) + guard_class(p1, ConstClass(node_vtable)) [] + setfield_gc(p3, p1, descr=nextdescr) + jump(p3) + """ + expected = """ + [p1] + guard_class(p1, ConstClass(node_vtable)) [] + i3 = getfield_gc(p1, descr=otherdescr) + jump(p1, i3) + """ + self.optimize_bridge(loop, bridge, expected) + + class TestLLtypeGuards(BaseTestGenerateGuards, LLtypeMixin): pass @@ -915,6 +1050,9 @@ pass class FakeOptimizer: + def __init__(self): + self.opaque_pointers = {} + self.values = {} def make_equal_to(*args): pass def getvalue(*args): diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py --- a/pypy/module/pypyjit/test_pypy_c/test_call.py +++ b/pypy/module/pypyjit/test_pypy_c/test_call.py @@ -374,10 +374,10 @@ p24 = new_array(1, descr=<ArrayP .>) p26 = new_with_vtable(ConstClass(W_ListObject)) setfield_gc(p0, i20, descr=<FieldS .*PyFrame.vable_token .*>) + setfield_gc(p22, 1, descr=<FieldU pypy.interpreter.argument.Arguments.inst__jit_few_keywords .*>) setfield_gc(p26, ConstPtr(ptr22), descr=<FieldP pypy.objspace.std.listobject.W_ListObject.inst_strategy .*>) setarrayitem_gc(p24, 0, p26, descr=<ArrayP .>) setfield_gc(p22, p24, descr=<FieldP .*Arguments.inst_arguments_w .*>) - setfield_gc(p22, 1, descr=<FieldU .*Arguments.inst__jit_few_keywords .*>) p32 = call_may_force(11376960, p18, p22, descr=<Callr . rr EF=6>) ... """) _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit