[pypy-svn] r67967 - in pypy/branch/better-getfield-opts/pypy/jit/metainterp: . test

cfbolz at codespeak.net cfbolz at codespeak.net
Tue Sep 29 10:41:58 CEST 2009


Author: cfbolz
Date: Tue Sep 29 10:41:57 2009
New Revision: 67967

Modified:
   pypy/branch/better-getfield-opts/pypy/jit/metainterp/optimizeopt.py
   pypy/branch/better-getfield-opts/pypy/jit/metainterp/test/test_optimizeopt.py
Log:
 - refactor a bit the way the getfield operations of non-virtuals are optimized.
   Not completely happy with it, but I didn't like the complete mixup in the old
   world.

 - add a optimization for getarrayitem operations similar to that of getfield
   (this is useful because then e.g. looking up the same global twice gets
   folded away, or looking up the same attribute of an instance). This is
   complicated by the fact that setarrayitem(X, (a constant), Y) does not have
   to invalidate everything.


Modified: pypy/branch/better-getfield-opts/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/branch/better-getfield-opts/pypy/jit/metainterp/optimizeopt.py	(original)
+++ pypy/branch/better-getfield-opts/pypy/jit/metainterp/optimizeopt.py	Tue Sep 29 10:41:57 2009
@@ -40,9 +40,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
@@ -162,6 +161,7 @@
 
 
 class AbstractVirtualStructValue(AbstractVirtualValue):
+    _attrs_ = ('_fields', )
 
     def __init__(self, optimizer, keybox, source_op=None):
         AbstractVirtualValue.__init__(self, optimizer, keybox, source_op)
@@ -350,14 +350,8 @@
         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.heap_op_optimizer = HeapOpOptimizer(self)
 
     def getinterned(self, box):
         constbox = self.get_constant_box(box)
@@ -481,6 +475,7 @@
         self.loop.operations = self.newoperations
 
     def emit_operation(self, op, must_clone=True):
+        self.heap_op_optimizer.emitting_operation(op)
         op1 = op
         for i in range(len(op.args)):
             arg = op.args[i]
@@ -528,21 +523,6 @@
         op2.suboperations = op1.suboperations
         op1.optimized = op2
 
-    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:
@@ -554,8 +534,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)
 
@@ -677,18 +655,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().
@@ -700,15 +668,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)
@@ -747,7 +708,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().
@@ -761,9 +722,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])
@@ -777,9 +737,143 @@
         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.GETFIELD_GC or opnum == rop.GETFIELD_GC_PURE or 
+            opnum == rop.SETFIELD_GC or
+            opnum == rop.GETARRAYITEM_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/branch/better-getfield-opts/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/branch/better-getfield-opts/pypy/jit/metainterp/test/test_optimizeopt.py	(original)
+++ pypy/branch/better-getfield-opts/pypy/jit/metainterp/test/test_optimizeopt.py	Tue Sep 29 10:41:57 2009
@@ -1075,21 +1075,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 = """
@@ -1261,6 +1268,117 @@
         """
         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_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