[pypy-svn] r69142 - in pypy/trunk/pypy/jit: backend/llgraph backend/test backend/x86 metainterp metainterp/test

arigo at codespeak.net arigo at codespeak.net
Wed Nov 11 10:31:10 CET 2009


Author: arigo
Date: Wed Nov 11 10:31:09 2009
New Revision: 69142

Modified:
   pypy/trunk/pypy/jit/backend/llgraph/llimpl.py
   pypy/trunk/pypy/jit/backend/test/runner_test.py
   pypy/trunk/pypy/jit/backend/test/test_ll_random.py
   pypy/trunk/pypy/jit/backend/x86/assembler.py
   pypy/trunk/pypy/jit/backend/x86/regalloc.py
   pypy/trunk/pypy/jit/metainterp/optimizeopt.py
   pypy/trunk/pypy/jit/metainterp/resoperation.py
   pypy/trunk/pypy/jit/metainterp/test/test_basic.py
   pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
Log:
Merge the 'merge-guards-2' branch:
merge guard_nonnull followed by guard_class and/or guard_value.
Needs a new guard, guard_nonnull_class.


Modified: pypy/trunk/pypy/jit/backend/llgraph/llimpl.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/llgraph/llimpl.py	(original)
+++ pypy/trunk/pypy/jit/backend/llgraph/llimpl.py	Wed Nov 11 10:31:09 2009
@@ -134,6 +134,7 @@
     'guard_overflow'       : ((), None),
     'guard_nonnull'        : (('ref',), None),
     'guard_isnull'        : (('ref',), None),
+    'guard_nonnull_class' : (('ref', 'ref'), None),
     'newstr'          : (('int',), 'ref'),
     'strlen'          : (('ref',), 'int'),
     'strgetitem'      : (('ref', 'int'), 'int'),
@@ -562,6 +563,11 @@
         if value.typeptr != expected_class:
             raise GuardFailed
 
+    def op_guard_nonnull_class(self, _, value, expected_class):
+        if not value:
+            raise GuardFailed
+        self.op_guard_class(_, value, expected_class)
+
     def op_guard_value(self, _, value, expected_value):
         if value != expected_value:
             raise GuardFailed

Modified: pypy/trunk/pypy/jit/backend/test/runner_test.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/test/runner_test.py	(original)
+++ pypy/trunk/pypy/jit/backend/test/runner_test.py	Wed Nov 11 10:31:09 2009
@@ -466,8 +466,8 @@
         #null_box = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, lltype.nullptr(T)))
         self.execute_operation(rop.GUARD_CLASS, [t_box, T_box], 'void')
         assert not self.guard_failed
-        #self.execute_operation(rop.GUARD_CLASS_INVERSE, [t_box, null_box],
-        #                       'void')
+        self.execute_operation(rop.GUARD_NONNULL_CLASS, [t_box, T_box], 'void')
+        assert not self.guard_failed
 
     def test_failing_guards(self):
         t_box, T_box = self.alloc_instance(self.T)
@@ -489,10 +489,12 @@
     def test_failing_guard_class(self):
         t_box, T_box = self.alloc_instance(self.T)
         u_box, U_box = self.alloc_instance(self.U)        
-        #null_box = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, lltype.nullptr(T)))
+        null_box = self.null_instance()
         for opname, args in [(rop.GUARD_CLASS, [t_box, U_box]),
                              (rop.GUARD_CLASS, [u_box, T_box]),
-                             #(rop.GUARD_VALUE_INVERSE, [BoxInt(10), BoxInt(10)]),
+                             (rop.GUARD_NONNULL_CLASS, [t_box, U_box]),
+                             (rop.GUARD_NONNULL_CLASS, [u_box, T_box]),
+                             (rop.GUARD_NONNULL_CLASS, [null_box, T_box]),
                              ]:
             assert self.execute_operation(opname, args, 'void') == None
             assert self.guard_failed

Modified: pypy/trunk/pypy/jit/backend/test/test_ll_random.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/test/test_ll_random.py	(original)
+++ pypy/trunk/pypy/jit/backend/test/test_ll_random.py	Wed Nov 11 10:31:09 2009
@@ -203,6 +203,21 @@
         op = ResOperation(self.opnum, [v, c_vtable2], None)
         return op, (vtable == vtable2)
 
+class GuardNonNullClassOperation(GuardClassOperation):
+    def gen_guard(self, builder, r):
+        if r.random() < 0.5:
+            return GuardClassOperation.gen_guard(self, builder, r)
+        else:
+            v = BoxPtr(lltype.nullptr(llmemory.GCREF.TO))
+            op = ResOperation(rop.SAME_AS, [ConstPtr(v.value)], v)
+            builder.loop.operations.append(op)
+            v2, S2 = builder.get_structptr_var(r, must_have_vtable=True)
+            vtable2 = S2._hints['vtable']._as_ptr()
+            c_vtable2 = ConstAddr(llmemory.cast_ptr_to_adr(vtable2),
+                                  builder.cpu)
+            op = ResOperation(self.opnum, [v, c_vtable2], None)
+            return op, False
+
 class GetFieldOperation(test_random.AbstractOperation):
     def field_descr(self, builder, r):
         v, S = builder.get_structptr_var(r)
@@ -577,6 +592,7 @@
     OPERATIONS.append(RaisingCallOperationGuardNoException(rop.CALL))
     OPERATIONS.append(RaisingCallOperationWrongGuardException(rop.CALL))
     OPERATIONS.append(CallOperationException(rop.CALL))
+OPERATIONS.append(GuardNonNullClassOperation(rop.GUARD_NONNULL_CLASS))
 
 LLtypeOperationBuilder.OPERATIONS = OPERATIONS
 

Modified: pypy/trunk/pypy/jit/backend/x86/assembler.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/x86/assembler.py	(original)
+++ pypy/trunk/pypy/jit/backend/x86/assembler.py	Wed Nov 11 10:31:09 2009
@@ -696,10 +696,10 @@
             self.mc.CMP(locs[0], locs[1])
         return self.implement_guard(addr, self.mc.JNE)
 
-    def genop_guard_guard_class(self, ign_1, guard_op, addr, locs, ign_2):
+    def _cmp_guard_class(self, mc, locs):
         offset = self.cpu.vtable_offset
         if offset is not None:
-            self.mc.CMP(mem(locs[0], offset), locs[1])
+            mc.CMP(mem(locs[0], offset), locs[1])
         else:
             # XXX hard-coded assumption: to go from an object to its class
             # we use the following algorithm:
@@ -714,8 +714,24 @@
             type_info_group = llop.gc_get_type_info_group(llmemory.Address)
             type_info_group = rffi.cast(lltype.Signed, type_info_group)
             expected_typeid = (classptr - type_info_group) >> 2
-            self.mc.CMP16(mem(locs[0], 0), imm32(expected_typeid))
-            #
+            mc.CMP16(mem(locs[0], 0), imm32(expected_typeid))
+
+    def genop_guard_guard_class(self, ign_1, guard_op, addr, locs, ign_2):
+        self._cmp_guard_class(self.mc._mc, locs)
+        return self.implement_guard(addr, self.mc.JNE)
+
+    def genop_guard_guard_nonnull_class(self, ign_1, guard_op,
+                                        addr, locs, ign_2):
+        mc = self.mc._mc
+        mc.CMP(locs[0], imm8(1))
+        mc.write(constlistofchars('\x72\x00'))             # JB later
+        jb_location = mc.get_relative_pos()
+        self._cmp_guard_class(mc, locs)
+        # patch the JB above
+        offset = mc.get_relative_pos() - jb_location
+        assert 0 < offset <= 127
+        mc.overwrite(jb_location-1, [chr(offset)])
+        #
         return self.implement_guard(addr, self.mc.JNE)
 
     def _no_const_locs(self, args):
@@ -880,8 +896,9 @@
         print msg
         raise NotImplementedError(msg)
 
-    def not_implemented_op_guard(self, op, regalloc, arglocs, resloc, descr):
-        msg = "not implemented operation (guard): %s" % op.getopname()
+    def not_implemented_op_guard(self, op, guard_op,
+                                 failaddr, arglocs, resloc):
+        msg = "not implemented operation (guard): %s" % guard_op.getopname()
         print msg
         raise NotImplementedError(msg)
 

Modified: pypy/trunk/pypy/jit/backend/x86/regalloc.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/x86/regalloc.py	(original)
+++ pypy/trunk/pypy/jit/backend/x86/regalloc.py	Wed Nov 11 10:31:09 2009
@@ -424,7 +424,9 @@
         y = self.loc(op.args[1])
         self.perform_guard(op, [x, y], None)
         self.rm.possibly_free_vars(op.args)
-    
+
+    consider_guard_nonnull_class = consider_guard_class
+
     def _consider_binop_part(self, op, ignored):
         x = op.args[0]
         argloc = self.loc(op.args[1])

Modified: pypy/trunk/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/optimizeopt.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/optimizeopt.py	Wed Nov 11 10:31:09 2009
@@ -42,8 +42,8 @@
 
 
 class OptValue(object):
-    _attrs_ = ('box', 'known_class', 'guard_class_index', 'level')
-    guard_class_index = -1
+    _attrs_ = ('box', 'known_class', 'last_guard_index', 'level')
+    last_guard_index = -1
 
     level = LEVEL_UNKNOWN
 
@@ -88,10 +88,15 @@
             return None
 
     def make_constant_class(self, classbox, opindex):
-        if self.level < LEVEL_KNOWNCLASS:
-            self.known_class = classbox
-            self.level = LEVEL_KNOWNCLASS
-            self.guard_class_index = opindex
+        assert self.level < LEVEL_KNOWNCLASS
+        self.known_class = classbox
+        self.level = LEVEL_KNOWNCLASS
+        self.last_guard_index = opindex
+
+    def make_nonnull(self, opindex):
+        assert self.level < LEVEL_NONNULL
+        self.level = LEVEL_NONNULL
+        self.last_guard_index = opindex
 
     def is_nonnull(self):
         level = self.level
@@ -104,7 +109,7 @@
         else:
             return False
 
-    def make_nonnull(self):
+    def ensure_nonnull(self):
         if self.level < LEVEL_NONNULL:
             self.level = LEVEL_NONNULL
 
@@ -577,17 +582,26 @@
         elif value.is_null():
             raise InvalidLoop
         self.emit_operation(op)
-        value.make_nonnull()
+        value.make_nonnull(len(self.newoperations) - 1)
 
     def optimize_GUARD_VALUE(self, op):
         value = self.getvalue(op.args[0])
         emit_operation = True
-        if value.guard_class_index != -1:
-            # there already has been a guard_class on this value, which is
-            # rather silly. replace the original guard_class with a guard_value
-            guard_class_op = self.newoperations[value.guard_class_index]
-            guard_class_op.opnum = op.opnum
-            guard_class_op.args[1] = op.args[1]
+        if value.last_guard_index != -1:
+            # there already has been a guard_nonnull or guard_class or
+            # guard_nonnull_class on this value, which is rather silly.
+            # replace the original guard with a guard_value
+            old_guard_op = self.newoperations[value.last_guard_index]
+            old_opnum = old_guard_op.opnum
+            old_guard_op.opnum = op.opnum
+            old_guard_op.args = [old_guard_op.args[0], op.args[1]]
+            if old_opnum == rop.GUARD_NONNULL:
+                # hack hack hack.  Change the guard_opnum on
+                # old_guard_op.descr so that when resuming,
+                # the operation is not skipped by pyjitpl.py.
+                descr = old_guard_op.descr
+                assert isinstance(descr, compile.ResumeGuardDescr)
+                descr.guard_opnum = rop.GUARD_NONNULL_CLASS
             emit_operation = False
         constbox = op.args[1]
         assert isinstance(constbox, Const)
@@ -610,8 +624,29 @@
             # earlier, in optimizefindnode.py.
             assert realclassbox.same_constant(expectedclassbox)
             return
-        self.emit_operation(op)
-        value.make_constant_class(expectedclassbox, len(self.newoperations) - 1)
+        emit_operation = True
+        if value.last_guard_index != -1:
+            # there already has been a guard_nonnull or guard_class or
+            # guard_nonnull_class on this value.
+            old_guard_op = self.newoperations[value.last_guard_index]
+            if old_guard_op.opnum == rop.GUARD_NONNULL:
+                # it was a guard_nonnull, which we replace with a
+                # guard_nonnull_class.
+                old_guard_op.opnum = rop.GUARD_NONNULL_CLASS
+                old_guard_op.args = [old_guard_op.args[0], op.args[1]]
+                # hack hack hack.  Change the guard_opnum on
+                # old_guard_op.descr so that when resuming,
+                # the operation is not skipped by pyjitpl.py.
+                descr = old_guard_op.descr
+                assert isinstance(descr, compile.ResumeGuardDescr)
+                descr.guard_opnum = rop.GUARD_NONNULL_CLASS
+                emit_operation = False
+        if emit_operation:
+            self.emit_operation(op)
+            last_guard_index = len(self.newoperations) - 1
+        else:
+            last_guard_index = value.last_guard_index
+        value.make_constant_class(expectedclassbox, last_guard_index)
 
     def optimize_GUARD_NO_EXCEPTION(self, op):
         if not self.exception_might_have_happened:
@@ -669,7 +704,7 @@
             assert fieldvalue is not None
             self.make_equal_to(op.result, fieldvalue)
         else:
-            value.make_nonnull()
+            value.ensure_nonnull()
             self.heap_op_optimizer.optimize_GETFIELD_GC(op, value)
 
     # note: the following line does not mean that the two operations are
@@ -681,7 +716,7 @@
         if value.is_virtual():
             value.setfield(op.descr, self.getvalue(op.args[1]))
         else:
-            value.make_nonnull()
+            value.ensure_nonnull()
             fieldvalue = self.getvalue(op.args[1])
             self.heap_op_optimizer.optimize_SETFIELD_GC(op, value, fieldvalue)
 
@@ -708,7 +743,7 @@
         if value.is_virtual():
             self.make_constant_int(op.result, value.getlength())
         else:
-            value.make_nonnull()
+            value.ensure_nonnull()
             self.optimize_default(op)
 
     def optimize_GETARRAYITEM_GC(self, op):
@@ -719,7 +754,7 @@
                 itemvalue = value.getitem(indexbox.getint())
                 self.make_equal_to(op.result, itemvalue)
                 return
-        value.make_nonnull()
+        value.ensure_nonnull()
         self.heap_op_optimizer.optimize_GETARRAYITEM_GC(op, value)
 
     # note: the following line does not mean that the two operations are
@@ -733,7 +768,7 @@
             if indexbox is not None:
                 value.setitem(indexbox.getint(), self.getvalue(op.args[2]))
                 return
-        value.make_nonnull()
+        value.ensure_nonnull()
         fieldvalue = self.getvalue(op.args[2])
         self.heap_op_optimizer.optimize_SETARRAYITEM_GC(op, value, fieldvalue)
 
@@ -873,7 +908,7 @@
             self.optimizer.make_equal_to(op.result, fieldvalue)
             return
         # default case: produce the operation
-        value.make_nonnull()
+        value.ensure_nonnull()
         self.optimizer.optimize_default(op)
         # then remember the result of reading the field
         fieldvalue = self.optimizer.getvalue(op.result)

Modified: pypy/trunk/pypy/jit/metainterp/resoperation.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/resoperation.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/resoperation.py	Wed Nov 11 10:31:09 2009
@@ -119,6 +119,7 @@
     'GUARD_CLASS',
     'GUARD_NONNULL',
     'GUARD_ISNULL',
+    'GUARD_NONNULL_CLASS',
     '_GUARD_FOLDABLE_LAST',
     'GUARD_NO_EXCEPTION',
     'GUARD_EXCEPTION',

Modified: pypy/trunk/pypy/jit/metainterp/test/test_basic.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_basic.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_basic.py	Wed Nov 11 10:31:09 2009
@@ -1003,10 +1003,10 @@
 
         class A(object):
             def g(self, x):
-                return x - 1
+                return x - 5
         class B(A):
             def g(self, y):
-                return y - 2
+                return y - 3
 
         a1 = A()
         a2 = A()
@@ -1021,8 +1021,125 @@
                 hint(a, promote=True)
             return x
         res = self.meta_interp(f, [299], listops=True)
+        assert res == f(299)
         self.check_loops(guard_class=0, guard_value=3)
 
+    def test_merge_guardnonnull_guardclass(self):
+        from pypy.rlib.objectmodel import instantiate
+        myjitdriver = JitDriver(greens = [], reds = ['x', 'l'])
+
+        class A(object):
+            def g(self, x):
+                return x - 3
+        class B(A):
+            def g(self, y):
+                return y - 5
+
+        a1 = A()
+        b1 = B()
+        def f(x):
+            l = [None] * 100 + [b1] * 100 + [a1] * 100
+            while x > 0:
+                myjitdriver.can_enter_jit(x=x, l=l)
+                myjitdriver.jit_merge_point(x=x, l=l)
+                a = l[x]
+                if a:
+                    x = a.g(x)
+                else:
+                    x -= 7
+            return x
+        res = self.meta_interp(f, [299], listops=True)
+        assert res == f(299)
+        self.check_loops(guard_class=0, guard_nonnull=0,
+                         guard_nonnull_class=2, guard_isnull=1)
+
+    def test_merge_guardnonnull_guardvalue(self):
+        from pypy.rlib.objectmodel import instantiate
+        myjitdriver = JitDriver(greens = [], reds = ['x', 'l'])
+
+        class A(object):
+            pass
+        class B(A):
+            pass
+
+        a1 = A()
+        b1 = B()
+        def f(x):
+            l = [b1] * 100 + [None] * 100 + [a1] * 100
+            while x > 0:
+                myjitdriver.can_enter_jit(x=x, l=l)
+                myjitdriver.jit_merge_point(x=x, l=l)
+                a = l[x]
+                if a:
+                    x -= 5
+                else:
+                    x -= 7
+                hint(a, promote=True)
+            return x
+        res = self.meta_interp(f, [299], listops=True)
+        assert res == f(299)
+        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=2,
+                         guard_nonnull_class=0, guard_isnull=1)
+
+    def test_merge_guardnonnull_guardvalue_2(self):
+        from pypy.rlib.objectmodel import instantiate
+        myjitdriver = JitDriver(greens = [], reds = ['x', 'l'])
+
+        class A(object):
+            pass
+        class B(A):
+            pass
+
+        a1 = A()
+        b1 = B()
+        def f(x):
+            l = [None] * 100 + [b1] * 100 + [a1] * 100
+            while x > 0:
+                myjitdriver.can_enter_jit(x=x, l=l)
+                myjitdriver.jit_merge_point(x=x, l=l)
+                a = l[x]
+                if a:
+                    x -= 5
+                else:
+                    x -= 7
+                hint(a, promote=True)
+            return x
+        res = self.meta_interp(f, [299], listops=True)
+        assert res == f(299)
+        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=2,
+                         guard_nonnull_class=0, guard_isnull=1)
+
+    def test_merge_guardnonnull_guardclass_guardvalue(self):
+        from pypy.rlib.objectmodel import instantiate
+        myjitdriver = JitDriver(greens = [], reds = ['x', 'l'])
+
+        class A(object):
+            def g(self, x):
+                return x - 3
+        class B(A):
+            def g(self, y):
+                return y - 5
+
+        a1 = A()
+        a2 = A()
+        b1 = B()
+        def f(x):
+            l = [a2] * 100 + [None] * 100 + [b1] * 100 + [a1] * 100
+            while x > 0:
+                myjitdriver.can_enter_jit(x=x, l=l)
+                myjitdriver.jit_merge_point(x=x, l=l)
+                a = l[x]
+                if a:
+                    x = a.g(x)
+                else:
+                    x -= 7
+                hint(a, promote=True)
+            return x
+        res = self.meta_interp(f, [399], listops=True)
+        assert res == f(399)
+        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=3,
+                         guard_nonnull_class=0, guard_isnull=1)
+
     def test_residual_call_doesnt_lose_info(self):
         myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'l'])
 

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	Wed Nov 11 10:31:09 2009
@@ -1472,6 +1472,56 @@
         """
         self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected)
 
+    def test_merge_guard_nonnull_guard_class(self):
+        ops = """
+        [p1, i0, i1, i2, p2]
+        guard_nonnull(p1) [i0]
+        i3 = int_add(i1, i2)
+        guard_class(p1, ConstClass(node_vtable)) [i1]
+        jump(p2, i0, i1, i3, p2)
+        """
+        expected = """
+        [p1, i0, i1, i2, p2]
+        guard_nonnull_class(p1, ConstClass(node_vtable)) [i0]
+        i3 = int_add(i1, i2)
+        jump(p2, i0, i1, i3, p2)
+        """
+        self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected)
+
+    def test_merge_guard_nonnull_guard_value(self):
+        ops = """
+        [p1, i0, i1, i2, p2]
+        guard_nonnull(p1) [i0]
+        i3 = int_add(i1, i2)
+        guard_value(p1, ConstPtr(myptr)) [i1]
+        jump(p2, i0, i1, i3, p2)
+        """
+        expected = """
+        [p1, i0, i1, i2, p2]
+        guard_value(p1, ConstPtr(myptr)) [i0]
+        i3 = int_add(i1, i2)
+        jump(p2, i0, i1, i3, p2)
+        """
+        self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected)
+
+    def test_merge_guard_nonnull_guard_class_guard_value(self):
+        ops = """
+        [p1, i0, i1, i2, p2]
+        guard_nonnull(p1) [i0]
+        i3 = int_add(i1, i2)
+        guard_class(p1, ConstClass(node_vtable)) [i2]
+        i4 = int_sub(i3, 1)
+        guard_value(p1, ConstPtr(myptr)) [i1]
+        jump(p2, i0, i1, i4, p2)
+        """
+        expected = """
+        [p1, i0, i1, i2, p2]
+        guard_value(p1, ConstPtr(myptr)) [i0]
+        i3 = int_add(i1, i2)
+        i4 = int_sub(i3, 1)
+        jump(p2, i0, i1, i4, p2)
+        """
+        self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected)
 
     # ----------
 



More information about the Pypy-commit mailing list