[pypy-svn] r68204 - in pypy/trunk/pypy: . jit/backend/llvm/test jit/backend/x86/test jit/metainterp jit/metainterp/test translator/backendopt translator/backendopt/test
pedronis at codespeak.net
pedronis at codespeak.net
Tue Oct 6 16:12:46 CEST 2009
Author: pedronis
Date: Tue Oct 6 16:12:45 2009
New Revision: 68204
Added:
pypy/trunk/pypy/translator/backendopt/test/test_writeanalyze.py
- copied unchanged from r68203, pypy/branch/better-getfield-opts/pypy/translator/backendopt/test/test_writeanalyze.py
pypy/trunk/pypy/translator/backendopt/writeanalyze.py
- copied unchanged from r68203, pypy/branch/better-getfield-opts/pypy/translator/backendopt/writeanalyze.py
Modified:
pypy/trunk/pypy/ (props changed)
pypy/trunk/pypy/jit/backend/llvm/test/conftest.py (props changed)
pypy/trunk/pypy/jit/backend/x86/test/test_gc_integration.py (props changed)
pypy/trunk/pypy/jit/metainterp/logger.py (props changed)
pypy/trunk/pypy/jit/metainterp/optimizeopt.py
pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
Log:
(cfbolz, pedronis): merge the better-getfield-ops branch.
Modified: pypy/trunk/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/optimizeopt.py (original)
+++ pypy/trunk/pypy/jit/metainterp/optimizeopt.py Tue Oct 6 16:12:45 2009
@@ -41,9 +41,8 @@
class OptValue(object):
- _attrs_ = ('box', 'known_class', 'level', '_fields')
+ _attrs_ = ('box', 'known_class', 'level')
level = LEVEL_UNKNOWN
- _fields = None
def __init__(self, box):
self.box = box
@@ -169,6 +168,7 @@
class AbstractVirtualStructValue(AbstractVirtualValue):
+ _attrs_ = ('_fields', )
def __init__(self, optimizer, keybox, source_op=None):
AbstractVirtualValue.__init__(self, optimizer, keybox, source_op)
@@ -357,15 +357,9 @@
self.cpu = cpu
self.loop = loop
self.values = {}
- # OptValues to clean when we see an operation with side-effects
- # they are ordered by fielddescrs of the affected fields
- # note: it is important that this is not a av_newdict2 dict!
- # we want more precision to not clear unrelated fields, just because
- # they are at the same offset (but in a different struct type)
- self.values_to_clean = {}
-
self.interned_refs = {}
self.resumedata_memo = resume.ResumeDataLoopMemo(cpu)
+ self.heap_op_optimizer = HeapOpOptimizer(self)
def getinterned(self, box):
constbox = self.get_constant_box(box)
@@ -497,6 +491,7 @@
self.loop.operations = self.newoperations
def emit_operation(self, op, must_clone=True):
+ self.heap_op_optimizer.emitting_operation(op)
for i in range(len(op.args)):
arg = op.args[i]
if arg in self.values:
@@ -520,21 +515,6 @@
newboxes = modifier.finish(self.values)
descr.store_final_boxes(op, newboxes)
- def clean_fields_of_values(self, descr=None):
- if descr is None:
- for descr, values in self.values_to_clean.iteritems():
- for value in values:
- value._fields.clear()
- self.values_to_clean = {}
- else:
- for value in self.values_to_clean.get(descr, []):
- del value._fields[descr]
- self.values_to_clean[descr] = []
-
- def register_value_to_clean(self, value, descr):
- self.values_to_clean.setdefault(descr, []).append(value)
-
-
def optimize_default(self, op):
if op.is_always_pure():
for arg in op.args:
@@ -546,8 +526,6 @@
resbox = execute_nonspec(self.cpu, op.opnum, argboxes, op.descr)
self.make_constant(op.result, resbox.constbox())
return
- elif not op.has_no_side_effect() and not op.is_ovf():
- self.clean_fields_of_values()
# otherwise, the operation remains
self.emit_operation(op)
@@ -666,18 +644,8 @@
assert fieldvalue is not None
self.make_equal_to(op.result, fieldvalue)
else:
- # check if the field was read from another getfield_gc just before
- if value._fields is None:
- value._fields = av_newdict2()
- elif op.descr in value._fields:
- self.make_equal_to(op.result, value._fields[op.descr])
- return
- # default case: produce the operation
value.make_nonnull()
- self.optimize_default(op)
- # then remember the result of reading the field
- value._fields[op.descr] = self.getvalue(op.result)
- self.register_value_to_clean(value, op.descr)
+ self.heap_op_optimizer.optimize_GETFIELD_GC(op, value)
# note: the following line does not mean that the two operations are
# completely equivalent, because GETFIELD_GC_PURE is_always_pure().
@@ -689,15 +657,8 @@
value.setfield(op.descr, self.getvalue(op.args[1]))
else:
value.make_nonnull()
- self.emit_operation(op)
- # kill all fields with the same descr, as those could be affected
- # by this setfield (via aliasing)
- self.clean_fields_of_values(op.descr)
- # remember the result of future reads of the field
- if value._fields is None:
- value._fields = av_newdict2()
- value._fields[op.descr] = self.getvalue(op.args[1])
- self.register_value_to_clean(value, op.descr)
+ fieldvalue = self.getvalue(op.args[1])
+ self.heap_op_optimizer.optimize_SETFIELD_GC(op, value, fieldvalue)
def optimize_NEW_WITH_VTABLE(self, op):
self.make_virtual(op.args[0], op.result, op)
@@ -736,7 +697,7 @@
self.make_equal_to(op.result, itemvalue)
return
value.make_nonnull()
- self.optimize_default(op)
+ self.heap_op_optimizer.optimize_GETARRAYITEM_GC(op, value)
# note: the following line does not mean that the two operations are
# completely equivalent, because GETARRAYITEM_GC_PURE is_always_pure().
@@ -750,9 +711,8 @@
value.setitem(indexbox.getint(), self.getvalue(op.args[2]))
return
value.make_nonnull()
- # don't use optimize_default, because otherwise unrelated struct
- # fields will be cleared
- self.emit_operation(op)
+ fieldvalue = self.getvalue(op.args[2])
+ self.heap_op_optimizer.optimize_SETARRAYITEM_GC(op, value, fieldvalue)
def optimize_INSTANCEOF(self, op):
value = self.getvalue(op.args[0])
@@ -766,9 +726,141 @@
self.emit_operation(op)
def optimize_DEBUG_MERGE_POINT(self, op):
- # special-case this operation to prevent e.g. the handling of
- # 'values_to_clean' (the op cannot be marked as side-effect-free)
- # otherwise it would be removed
- self.newoperations.append(op)
+ self.emit_operation(op)
optimize_ops = _findall(Optimizer, 'optimize_')
+
+
+class CachedArrayItems(object):
+ def __init__(self):
+ self.fixed_index_items = {}
+ self.var_index_item = None
+ self.var_index_indexvalue = None
+
+
+class HeapOpOptimizer(object):
+ def __init__(self, optimizer):
+ self.optimizer = optimizer
+ # cached OptValues for each field descr
+ # NOTE: it is important that this is not a av_newdict2 dict!
+ # we want more precision to prevent mixing up of unrelated fields, just
+ # because they are at the same offset (but in a different struct type)
+ self.cached_fields = {}
+
+ # cached OptValues for each field descr
+ self.cached_arrayitems = {}
+
+ def clean_caches(self):
+ self.cached_fields.clear()
+ self.cached_arrayitems.clear()
+
+ def cache_field_value(self, descr, value, fieldvalue, write=False):
+ if write:
+ d = self.cached_fields[descr] = {}
+ else:
+ d = self.cached_fields.setdefault(descr, {})
+ d[value] = fieldvalue
+
+ def read_cached_field(self, descr, value):
+ d = self.cached_fields.get(descr, None)
+ if d is None:
+ return None
+ return d.get(value, None)
+
+ def cache_arrayitem_value(self, descr, value, indexvalue, fieldvalue, write=False):
+ d = self.cached_arrayitems.get(descr, None)
+ if d is None:
+ d = self.cached_arrayitems[descr] = {}
+ cache = d.get(value, None)
+ if cache is None:
+ cache = d[value] = CachedArrayItems()
+ indexbox = self.optimizer.get_constant_box(indexvalue.box)
+ if indexbox is not None:
+ index = indexbox.getint()
+ if write:
+ for value, othercache in d.iteritems():
+ # fixed index, clean the variable index cache, in case the
+ # index is the same
+ othercache.var_index_indexvalue = None
+ othercache.var_index_item = None
+ try:
+ del othercache.fixed_index_items[index]
+ except KeyError:
+ pass
+ cache.fixed_index_items[index] = fieldvalue
+ else:
+ if write:
+ for value, othercache in d.iteritems():
+ # variable index, clear all caches for this descr
+ othercache.var_index_indexvalue = None
+ othercache.var_index_item = None
+ othercache.fixed_index_items.clear()
+ cache.var_index_indexvalue = indexvalue
+ cache.var_index_item = fieldvalue
+
+ def read_cached_arrayitem(self, descr, value, indexvalue):
+ d = self.cached_arrayitems.get(descr, None)
+ if d is None:
+ return None
+ cache = d.get(value, None)
+ if cache is None:
+ return None
+ indexbox = self.optimizer.get_constant_box(indexvalue.box)
+ if indexbox is not None:
+ return cache.fixed_index_items.get(indexbox.getint(), None)
+ elif cache.var_index_indexvalue is indexvalue:
+ return cache.var_index_item
+ return None
+
+ def emitting_operation(self, op):
+ if op.is_always_pure():
+ return
+ if op.has_no_side_effect():
+ return
+ if op.is_ovf():
+ return
+ if op.is_guard():
+ return
+ opnum = op.opnum
+ if (opnum == rop.SETFIELD_GC or
+ opnum == rop.SETARRAYITEM_GC or
+ opnum == rop.DEBUG_MERGE_POINT):
+ return
+ self.clean_caches()
+
+ def optimize_GETFIELD_GC(self, op, value):
+ # check if the field was read from another getfield_gc just before
+ # or has been written to recently
+ fieldvalue = self.read_cached_field(op.descr, value)
+ if fieldvalue is not None:
+ self.optimizer.make_equal_to(op.result, fieldvalue)
+ return
+ # default case: produce the operation
+ value.make_nonnull()
+ self.optimizer.optimize_default(op)
+ # then remember the result of reading the field
+ fieldvalue = self.optimizer.getvalue(op.result)
+ self.cache_field_value(op.descr, value, fieldvalue)
+
+ def optimize_SETFIELD_GC(self, op, value, fieldvalue):
+ self.optimizer.emit_operation(op)
+ # remember the result of future reads of the field
+ self.cache_field_value(op.descr, value, fieldvalue, write=True)
+
+ def optimize_GETARRAYITEM_GC(self, op, value):
+ indexvalue = self.optimizer.getvalue(op.args[1])
+ fieldvalue = self.read_cached_arrayitem(op.descr, value, indexvalue)
+ if fieldvalue is not None:
+ self.optimizer.make_equal_to(op.result, fieldvalue)
+ return
+ self.optimizer.optimize_default(op)
+ fieldvalue = self.optimizer.getvalue(op.result)
+ self.cache_arrayitem_value(op.descr, value, indexvalue, fieldvalue)
+
+ def optimize_SETARRAYITEM_GC(self, op, value, fieldvalue):
+ self.optimizer.emit_operation(op)
+ indexvalue = self.optimizer.getvalue(op.args[1])
+ self.cache_arrayitem_value(op.descr, value, indexvalue, fieldvalue,
+ write=True)
+
+
Modified: pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py Tue Oct 6 16:12:45 2009
@@ -1023,21 +1023,28 @@
def test_duplicate_getfield_1(self):
ops = """
- [p1]
+ [p1, p2]
i1 = getfield_gc(p1, descr=valuedescr)
- i2 = getfield_gc(p1, descr=valuedescr)
+ i2 = getfield_gc(p2, descr=valuedescr)
+ i3 = getfield_gc(p1, descr=valuedescr)
+ i4 = getfield_gc(p2, descr=valuedescr)
escape(i1)
escape(i2)
- jump(p1)
+ escape(i3)
+ escape(i4)
+ jump(p1, p2)
"""
expected = """
- [p1]
+ [p1, p2]
i1 = getfield_gc(p1, descr=valuedescr)
+ i2 = getfield_gc(p2, descr=valuedescr)
escape(i1)
+ escape(i2)
escape(i1)
- jump(p1)
+ escape(i2)
+ jump(p1, p2)
"""
- self.optimize_loop(ops, 'Not', expected)
+ self.optimize_loop(ops, 'Not, Not', expected)
def test_getfield_after_setfield(self):
ops = """
@@ -1206,6 +1213,138 @@
"""
self.optimize_loop(ops, 'Not, Not', ops)
+ def test_duplicate_getarrayitem_1(self):
+ ops = """
+ [p1]
+ p2 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p3 = getarrayitem_gc(p1, 1, descr=arraydescr2)
+ p4 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p5 = getarrayitem_gc(p1, 1, descr=arraydescr2)
+ escape(p2)
+ escape(p3)
+ escape(p4)
+ escape(p5)
+ jump(p1)
+ """
+ expected = """
+ [p1]
+ p2 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p3 = getarrayitem_gc(p1, 1, descr=arraydescr2)
+ escape(p2)
+ escape(p3)
+ escape(p2)
+ escape(p3)
+ jump(p1)
+ """
+ self.optimize_loop(ops, 'Not', expected)
+
+ def test_duplicate_getarrayitem_after_setarrayitem_1(self):
+ ops = """
+ [p1, p2]
+ setarrayitem_gc(p1, 0, p2, descr=arraydescr2)
+ p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ escape(p3)
+ jump(p1, p3)
+ """
+ expected = """
+ [p1, p2]
+ setarrayitem_gc(p1, 0, p2, descr=arraydescr2)
+ escape(p2)
+ jump(p1, p2)
+ """
+ self.optimize_loop(ops, 'Not, Not', expected)
+
+ def test_duplicate_getarrayitem_after_setarrayitem_2(self):
+ ops = """
+ [p1, p2, p3, i1]
+ setarrayitem_gc(p1, 0, p2, descr=arraydescr2)
+ setarrayitem_gc(p1, i1, p3, descr=arraydescr2)
+ p4 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p5 = getarrayitem_gc(p1, i1, descr=arraydescr2)
+ escape(p4)
+ escape(p5)
+ jump(p1, p2, p3, i1)
+ """
+ expected = """
+ [p1, p2, p3, i1]
+ setarrayitem_gc(p1, 0, p2, descr=arraydescr2)
+ setarrayitem_gc(p1, i1, p3, descr=arraydescr2)
+ p4 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ escape(p4)
+ escape(p3)
+ jump(p1, p2, p3, i1)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+
+ def test_duplicate_getarrayitem_after_setarrayitem_3(self):
+ ops = """
+ [p1, p2, p3, p4, i1]
+ setarrayitem_gc(p1, i1, p2, descr=arraydescr2)
+ setarrayitem_gc(p1, 0, p3, descr=arraydescr2)
+ setarrayitem_gc(p1, 1, p4, descr=arraydescr2)
+ p5 = getarrayitem_gc(p1, i1, descr=arraydescr2)
+ p6 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p7 = getarrayitem_gc(p1, 1, descr=arraydescr2)
+ escape(p5)
+ escape(p6)
+ escape(p7)
+ jump(p1, p2, p3, p4, i1)
+ """
+ expected = """
+ [p1, p2, p3, p4, i1]
+ setarrayitem_gc(p1, i1, p2, descr=arraydescr2)
+ setarrayitem_gc(p1, 0, p3, descr=arraydescr2)
+ setarrayitem_gc(p1, 1, p4, descr=arraydescr2)
+ p5 = getarrayitem_gc(p1, i1, descr=arraydescr2)
+ escape(p5)
+ escape(p3)
+ escape(p4)
+ jump(p1, p2, p3, p4, i1)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not, Not', expected)
+
+ def test_getarrayitem_pure_does_not_invalidate(self):
+ ops = """
+ [p1, p2]
+ p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ i4 = getfield_gc_pure(ConstPtr(myptr), descr=valuedescr)
+ p5 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ escape(p3)
+ escape(i4)
+ escape(p5)
+ jump(p1, p2)
+ """
+ expected = """
+ [p1, p2]
+ p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ escape(p3)
+ escape(5)
+ escape(p3)
+ jump(p1, p2)
+ """
+ self.optimize_loop(ops, 'Not, Not', expected)
+
+ def test_duplicate_getarrayitem_after_setarrayitem_two_arrays(self):
+ ops = """
+ [p1, p2, p3, p4, i1]
+ setarrayitem_gc(p1, 0, p3, descr=arraydescr2)
+ setarrayitem_gc(p2, 1, p4, descr=arraydescr2)
+ p5 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p6 = getarrayitem_gc(p2, 1, descr=arraydescr2)
+ escape(p5)
+ escape(p6)
+ jump(p1, p2, p3, p4, i1)
+ """
+ expected = """
+ [p1, p2, p3, p4, i1]
+ setarrayitem_gc(p1, 0, p3, descr=arraydescr2)
+ setarrayitem_gc(p2, 1, p4, descr=arraydescr2)
+ escape(p3)
+ escape(p4)
+ jump(p1, p2, p3, p4, i1)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not, Not', expected)
+
def test_bug_1(self):
ops = """
[i0, p1]
More information about the Pypy-commit
mailing list