[pypy-commit] pypy default: Merge jit-opaque-licm. It allows the heap optimizer to cache getitems of opaque pointers across loop boundaries when their class is known.
hakanardo
noreply at buildbot.pypy.org
Tue Jul 24 19:07:11 CEST 2012
Author: Hakan Ardo <hakan at 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>)
...
""")
More information about the pypy-commit
mailing list