[pypy-svn] r66449 - in pypy/branch/pyjitpl5-optimize4/pypy/jit/metainterp: . test

arigo at codespeak.net arigo at codespeak.net
Mon Jul 20 18:24:23 CEST 2009


Author: arigo
Date: Mon Jul 20 18:24:23 2009
New Revision: 66449

Modified:
   pypy/branch/pyjitpl5-optimize4/pypy/jit/metainterp/optimizeopt.py
   pypy/branch/pyjitpl5-optimize4/pypy/jit/metainterp/test/test_optimizeopt.py
Log:
Passes the first virtual tests.  Yay!


Modified: pypy/branch/pyjitpl5-optimize4/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/branch/pyjitpl5-optimize4/pypy/jit/metainterp/optimizeopt.py	(original)
+++ pypy/branch/pyjitpl5-optimize4/pypy/jit/metainterp/optimizeopt.py	Mon Jul 20 18:24:23 2009
@@ -1,27 +1,69 @@
-from pypy.jit.metainterp.history import Const, Box
+from pypy.jit.metainterp.history import Const, Box, BoxInt, BoxPtr, BoxObj
+from pypy.jit.metainterp.history import AbstractValue
 from pypy.jit.metainterp.resoperation import rop
+from pypy.jit.metainterp.specnode import SpecNode
+from pypy.jit.metainterp.specnode import VirtualInstanceSpecNode
 from pypy.jit.metainterp.optimizeutil import av_newdict, _findall
 from pypy.rlib.objectmodel import we_are_translated
 
 
-def optimize(loop):
+def optimize(cpu, loop):
     """Optimize loop.operations to make it match the input of loop.specnodes
     and to remove internal overheadish operations.  Note that loop.specnodes
     must be applicable to the loop; you will probably get an AssertionError
     if not.
     """
-    Optimizer().optimize(loop)
+    optimizer = Optimizer(cpu, loop)
+    optimizer.setup_virtuals()
+    optimizer.propagate_forward()
 
 # ____________________________________________________________
 
+class VirtualBox(AbstractValue):
+    def __init__(self):
+        self.fields = av_newdict()     # ofs -> Box
+
+    def nonnull(self):
+        return True
+
+
+class __extend__(SpecNode):
+    def setup_virtual_node(self, optimizer, box, newinputargs):
+        newinputargs.append(box)
+    def teardown_virtual_node(self, box, newexitargs):
+        newexitargs.append(box)
+
+class __extend__(VirtualInstanceSpecNode):
+    def setup_virtual_node(self, optimizer, box, newinputargs):
+        vbox = optimizer.make_virtual(box, self.known_class)
+        for ofs, subspecnode in self.fields:
+            subbox = optimizer.make_box(ofs)
+            vbox.fields[ofs] = subbox
+            subspecnode.setup_virtual_node(optimizer, subbox, newinputargs)
+    def teardown_virtual_node(self, box, newexitargs):
+        assert isinstance(box, VirtualBox)
+        for ofs, subspecnode in self.fields:
+            # XXX if ofs not in box.fields:...
+            subspecnode.teardown_virtual_node(box.fields[ofs], newexitargs)
+
 
 class Optimizer(object):
 
-    def __init__(self):
-        self._equals = {}          # mapping Box -> Box-or-Const
-        self._known_classes = {}   # mapping Box -> ConstClass
+    def __init__(self, cpu, loop):
+        self.cpu = cpu
+        self.loop = loop
+        # Boxes used a keys to _equals have been proven to be equal to
+        # something else: another Box, a Const, or a VirtualBox.  Boxes
+        # and VirtualBoxes can also be listed in _known_classes if we
+        # know their class (or just know that they are non-null, in which
+        # case we use None).  Boxes *must not* be keys in both dicts.
+        self._equals = {}          # mapping Box -> Box/Const/VirtualBox
+        self._known_classes = {}   # mapping Box -> ConstClass/None
 
     def deref(self, box):
+        """Maps a Box/Const to the corresponding Box/Const/VirtualBox
+        by following the dict _equals.
+        """
         while box in self._equals:
             # follow the chain: box -> box2 -> box3 -> ...
             box2 = self._equals[box]
@@ -33,24 +75,67 @@
             box = box3
         return box
 
-    def make_constant(self, box):
+    def _make_equal(self, box, box2):
         assert isinstance(box, Box)
         assert box not in self._equals
-        self._equals[box] = box.constbox()
+        assert box not in self._known_classes
+        self._equals[box] = box2
+
+    def make_constant(self, box):
+        """Mark the given Box as actually representing a Const value."""
+        self._make_equal(box, box.constbox())
+
+    def make_virtual(self, box, clsbox):
+        """Mark the given Box as actually representing a VirtualBox value."""
+        vbox = VirtualBox()
+        self._make_equal(box, vbox)
+        self.make_constant_class(vbox, clsbox)
+        return vbox
 
     def has_constant_class(self, box):
-        return isinstance(box, Const) or box in self._known_classes
+        return (isinstance(box, Const) or
+                self._known_classes.get(box, None) is not None)
 
     def make_constant_class(self, box, clsbox):
-        assert isinstance(box, Box)
         assert isinstance(clsbox, Const)
+        assert box not in self._equals
         self._known_classes[box] = clsbox
 
+    def make_nonnull(self, box):
+        assert box not in self._equals
+        self._known_classes.setdefault(box, None)
+
+    def make_box(self, fieldofs):
+        if fieldofs.is_pointer_field():
+            if not self.cpu.is_oo:
+                return BoxPtr()
+            else:
+                return BoxObj()
+        else:
+            return BoxInt()
+
+    def known_nonnull(self, box):
+        if isinstance(box, Box):
+            return box in self._known_classes
+        else:
+            return box.nonnull()   # Consts or VirtualBoxes
+
     # ----------
 
-    def optimize(self, loop):
+    def setup_virtuals(self):
+        inputargs = self.loop.inputargs
+        specnodes = self.loop.specnodes
+        assert len(inputargs) == len(specnodes)
+        newinputargs = []
+        for i in range(len(inputargs)):
+            specnodes[i].setup_virtual_node(self, inputargs[i], newinputargs)
+        self.loop.inputargs = newinputargs
+
+    # ----------
+
+    def propagate_forward(self):
         self.newoperations = []
-        for op in loop.operations:
+        for op in self.loop.operations:
             op2 = op.clone()
             op2.args = [self.deref(box) for box in op.args]
             opnum = op2.opnum
@@ -60,9 +145,11 @@
                     break
             else:
                 self.optimize_default(op2)
-        loop.operations = self.newoperations
+        self.loop.operations = self.newoperations
 
     def emit_operation(self, op):
+        for x in op.args:
+            assert not isinstance(x, VirtualBox)
         self.newoperations.append(op)
 
     def optimize_default(self, op):
@@ -77,6 +164,16 @@
         # otherwise, the operation remains
         self.emit_operation(op)
 
+    def optimize_JUMP(self, op):
+        orgop = self.loop.operations[-1]
+        exitargs = []
+        specnodes = orgop.jump_target.specnodes
+        assert len(op.args) == len(specnodes)
+        for i in range(len(specnodes)):
+            specnodes[i].teardown_virtual_node(op.args[i], exitargs)
+        op.args = exitargs
+        self.emit_operation(op)
+
     def optimize_GUARD_VALUE(self, op):
         assert isinstance(op.args[1], Const)
         assert op.args[0].get_() == op.args[1].get_()
@@ -119,11 +216,14 @@
             self.optimize_default(op)
 
     def optimize_OOISNOT(self, op):
-        if isinstance(op.args[1], Const) and not op.args[1].nonnull():
+        if (isinstance(op.args[0], VirtualBox) or
+            isinstance(op.args[1], VirtualBox)):
+            self.make_constant(op.result)
+        elif self.known_nonnull(op.args[1]):
             op.opnum = rop.OONONNULL
             del op.args[1]
             self.optimize_OONONNULL(op)
-        elif isinstance(op.args[0], Const) and not op.args[0].nonnull():
+        elif self.known_nonnull(op.args[0]):
             op.opnum = rop.OONONNULL
             del op.args[0]
             self.optimize_OONONNULL(op)
@@ -131,16 +231,35 @@
             self.optimize_default(op)
 
     def optimize_OOIS(self, op):
-        if isinstance(op.args[1], Const) and not op.args[1].nonnull():
+        if (isinstance(op.args[0], VirtualBox) or
+            isinstance(op.args[1], VirtualBox)):
+            self.make_constant(op.result)
+        elif self.known_nonnull(op.args[1]):
             op.opnum = rop.OOISNULL
             del op.args[1]
             self.optimize_OOISNULL(op)
-        elif isinstance(op.args[0], Const) and not op.args[0].nonnull():
+        elif self.known_nonnull(op.args[0]):
             op.opnum = rop.OOISNULL
             del op.args[0]
             self.optimize_OOISNULL(op)
         else:
             self.optimize_default(op)
 
+    def optimize_GETFIELD_GC(self, op):
+        instbox = op.args[0]
+        if isinstance(instbox, VirtualBox):
+            self._make_equal(op.result, instbox.fields[op.descr])
+        else:
+            self.optimize_default(op)
+
+    optimize_GETFIELD_PURE_GC = optimize_GETFIELD_GC
+
+    def optimize_SETFIELD_GC(self, op):
+        instbox = op.args[0]
+        if isinstance(instbox, VirtualBox):
+            instbox.fields[op.descr] = op.args[1]
+        else:
+            self.optimize_default(op)
+
 
 optimize_ops = _findall(Optimizer, 'optimize_')

Modified: pypy/branch/pyjitpl5-optimize4/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/branch/pyjitpl5-optimize4/pypy/jit/metainterp/test/test_optimizeopt.py	(original)
+++ pypy/branch/pyjitpl5-optimize4/pypy/jit/metainterp/test/test_optimizeopt.py	Mon Jul 20 18:24:23 2009
@@ -8,7 +8,7 @@
 
 # ____________________________________________________________
 
-def equaloplists(oplist1, oplist2):
+def equaloplists(oplist1, oplist2, remap={}):
     print '-'*20, 'Comparing lists', '-'*20
     for op1, op2 in zip(oplist1, oplist2):
         txt1 = str(op1)
@@ -20,11 +20,11 @@
         assert op1.opnum == op2.opnum
         assert len(op1.args) == len(op2.args)
         for x, y in zip(op1.args, op2.args):
-            assert x == y
-        assert op1.result == op2.result
+            assert x == remap.get(y, y)
+        assert op1.result == remap.get(op2.result, op2.result)
         assert op1.descr == op2.descr
         if op1.suboperations:
-            assert equaloplists(op1.suboperations, op2.suboperations)
+            assert equaloplists(op1.suboperations, op2.suboperations, remap)
     assert len(oplist1) == len(oplist2)
     print '-'*57
     return True
@@ -45,20 +45,57 @@
     py.test.raises(AssertionError,
                    "equaloplists(loop1.operations, loop3.operations)")
 
+def test_equaloplists_remap():
+    ops1 = """
+    [i0]
+    i1 = int_add(i0, 1)
+    guard_true(i1)
+        i2 = int_add(i1, 1)
+        fail(i2)
+    jump(i1)
+    """
+    ops2 = """
+    [i3]
+    i1 = int_add(i3, 1)
+    guard_true(i1)
+        i5 = int_add(i1, 1)
+        fail(i5)
+    jump(i1)
+    """
+    loop1 = parse(ops1)
+    loop2 = parse(ops2)
+    py.test.raises(AssertionError,
+                   "equaloplists(loop1.operations, loop2.operations)")
+    i0 = loop1.inputargs[0]
+    i3 = loop2.inputargs[0]
+    i2 = loop1.operations[1].suboperations[0].result
+    i5 = loop2.operations[1].suboperations[0].result
+    assert equaloplists(loop1.operations, loop2.operations,
+                        {i3: i0, i5: i2})
+
 # ____________________________________________________________
 
 class BaseTestOptimizeOpt(BaseTest):
 
     def assert_equal(self, optimized, expected):
-        assert optimized.inputargs == expected.inputargs
+        assert len(optimized.inputargs) == len(expected.inputargs)
+        remap = {}
+        for box1, box2 in zip(optimized.inputargs, expected.inputargs):
+            assert box1.__class__ == box2.__class__
+            remap[box2] = box1
         assert equaloplists(optimized.operations,
-                            expected.operations)
+                            expected.operations,
+                            remap)
 
-    def optimize(self, ops, spectext, optops, boxkinds=None, **values):
+    def optimize_loop(self, ops, spectext, optops, boxkinds=None, **values):
         loop = self.parse(ops, boxkinds=boxkinds)
         loop.setvalues(**values)
         loop.specnodes = self.unpack_specnodes(spectext)
-        optimize(loop)
+        assert loop.operations[-1].opnum == rop.JUMP
+        loop.operations[-1].jump_target = loop
+        #
+        optimize(self.cpu, loop)
+        #
         expected = self.parse(optops, boxkinds=boxkinds)
         self.assert_equal(loop, expected)
 
@@ -70,7 +107,7 @@
           fail(i0)
         jump(i)
         """
-        self.optimize(ops, 'Not', ops, i0=0)
+        self.optimize_loop(ops, 'Not', ops, i0=0)
 
     def test_constant_propagate(self):
         ops = """
@@ -90,7 +127,7 @@
         []
         jump()
         """
-        self.optimize(ops, '', expected, i0=5, i1=1, i2=0)
+        self.optimize_loop(ops, '', expected, i0=5, i1=1, i2=0)
 
     def test_constfold_all(self):
         for op in range(rop.INT_ADD, rop.BOOL_NOT+1):
@@ -107,7 +144,7 @@
             []
             jump()
             """
-            self.optimize(ops, '', expected)
+            self.optimize_loop(ops, '', expected)
 
     # ----------
 
@@ -126,7 +163,7 @@
           fail()
         jump(p0)
         """
-        self.optimize(ops, 'Not', expected)
+        self.optimize_loop(ops, 'Not', expected)
 
     def test_remove_consecutive_guard_value_constfold(self):
         ops = """
@@ -145,7 +182,7 @@
             fail()
         jump(3)
         """
-        self.optimize(ops, 'Not', expected, i0=0, i1=1, i2=3)
+        self.optimize_loop(ops, 'Not', expected, i0=0, i1=1, i2=3)
 
     def test_ooisnull_oononnull_1(self):
         ops = """
@@ -166,7 +203,7 @@
           fail()
         jump(p0)
         """
-        self.optimize(ops, 'Not', expected, i0=1, i1=0)
+        self.optimize_loop(ops, 'Not', expected, i0=1, i1=0)
 
     def test_ooisnull_oononnull_2(self):
         py.test.skip("less important")
@@ -187,7 +224,7 @@
           fail()
         jump(p0)
         """
-        self.optimize(ops, 'Not', expected, i0=1, i1=0)
+        self.optimize_loop(ops, 'Not', expected, i0=1, i1=0)
 
     def test_oois_1(self):
         ops = """
@@ -214,7 +251,84 @@
           fail()
         jump(p0)
         """
-        self.optimize(ops, 'Not', expected, i0=1, i1=0, i2=1, i3=0)
+        self.optimize_loop(ops, 'Not', expected, i0=1, i1=0, i2=1, i3=0)
+
+    # ----------
+
+    def test_virtual_1(self):
+        ops = """
+        [i, p0]
+        i0 = getfield_gc(p0, descr=valuedescr)
+        i1 = int_add(i0, i)
+        setfield_gc(p0, i1, descr=valuedescr)
+        jump(i, p0)
+        """
+        expected = """
+        [i, i2]
+        i1 = int_add(i2, i)
+        jump(i, i1)
+        """
+        self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)',
+                           expected)
+
+    def test_virtual_oois(self):
+        ops = """
+        [p0, p1, p2]
+        i1 = oononnull(p0)
+        guard_true(i1)
+          fail()
+        i2 = ooisnull(p0)
+        guard_false(i2)
+          fail()
+        i3 = ooisnot(p0, NULL)
+        guard_true(i3)
+          fail()
+        i4 = oois(p0, NULL)
+        guard_false(i4)
+          fail()
+        i5 = ooisnot(NULL, p0)
+        guard_true(i5)
+          fail()
+        i6 = oois(NULL, p0)
+        guard_false(i6)
+          fail()
+        i7 = ooisnot(p0, p1)
+        guard_true(i7)
+          fail()
+        i8 = oois(p0, p1)
+        guard_false(i8)
+          fail()
+        i9 = ooisnot(p0, p2)
+        guard_true(i9)
+          fail()
+        i10 = oois(p0, p2)
+        guard_false(i10)
+          fail()
+        i11 = ooisnot(p2, p1)
+        guard_true(i11)
+          fail()
+        i12 = oois(p2, p1)
+        guard_false(i12)
+          fail()
+        jump(p0, p1, p2)
+        """
+        expected = """
+        [p2]
+        # all constant-folded :-)
+        jump(p2)
+        """
+        self.optimize_loop(ops, '''Virtual(node_vtable),
+                                   Virtual(node_vtable),
+                                   Not''',
+                           expected,
+                           i1=1, i2=0, i3=1, i4=0, i5=1, i6=0,
+                           i7=1, i8=0, i9=1, i10=0, i11=1, i12=0)
+        #
+        # to be complete, we also check the no-opt case where most comparisons
+        # are not removed.  The exact set of comparisons removed depends on
+        # the details of the algorithm...
+        expected2 = ops
+        self.optimize_loop(ops, 'Not, Not, Not', expected2)
 
 
 class TestLLtype(BaseTestOptimizeOpt, LLtypeMixin):



More information about the Pypy-commit mailing list