Commits

Carl Friedrich Bolz  committed 93a45b7

make the JIT aware of the downcasts that are present in rtyped-flowgraphs. This
allows the JIT to sometimes find out the class of a variable without having to
produce a guard_class.

  • Participants
  • Parent commits 92f0d05

Comments (0)

Files changed (8)

File pypy/jit/codewriter/jtransform.py

         if op.args[0] in self.vable_array_vars:
             self.vable_array_vars[op.result]= self.vable_array_vars[op.args[0]]
 
-    rewrite_op_cast_pointer = rewrite_op_same_as
+    def rewrite_op_cast_pointer(self, op):
+        newop = self.rewrite_op_same_as(op)
+        assert newop is None
+        if (self._is_rclass_instance(op.args[0]) and
+                self._is_rclass_instance(op.result)):
+            FROM = op.args[0].concretetype.TO
+            TO = op.result.concretetype.TO
+            if lltype._castdepth(TO, FROM) > 0:
+                vtable = heaptracker.get_vtable_for_gcstruct(self.cpu, TO)
+                const_vtable = Constant(vtable, lltype.typeOf(vtable))
+                return [None, # hack, do the right renaming from op.args[0] to op.result
+                        SpaceOperation("record_known_class", [op.args[0], const_vtable], None)]
+
     def rewrite_op_cast_bool_to_int(self, op): pass
     def rewrite_op_cast_bool_to_uint(self, op): pass
     def rewrite_op_cast_char_to_int(self, op): pass

File pypy/jit/metainterp/blackhole.py

     @arguments("r")
     def bhimpl_mark_opaque_ptr(a):
         pass
+    @arguments("r", "i")
+    def bhimpl_record_known_class(a, b):
+        pass
 
     @arguments("i", returns="i")
     def bhimpl_int_copy(a):

File pypy/jit/metainterp/optimizeopt/rewrite.py

     def optimize_GUARD_FALSE(self, op):
         self.optimize_guard(op, CONST_0)
 
+    def optimize_RECORD_KNOWN_CLASS(self, op):
+        value = self.getvalue(op.getarg(0))
+        expectedclassbox = op.getarg(1)
+        assert isinstance(expectedclassbox, Const)
+        realclassbox = value.get_constant_class(self.optimizer.cpu)
+        if realclassbox is not None:
+            assert realclassbox.same_constant(expectedclassbox)
+            return
+        value.make_constant_class(expectedclassbox, None)
+
     def optimize_GUARD_CLASS(self, op):
         value = self.getvalue(op.getarg(0))
         expectedclassbox = op.getarg(1)

File pypy/jit/metainterp/optimizeopt/simplify.py

     def optimize_MARK_OPAQUE_PTR(self, op):
         pass
 
+    def optimize_RECORD_KNOWN_CLASS(self, op):
+        pass
+
 
 dispatch_opt = make_dispatcher_method(OptSimplify, 'optimize_',
         default=OptSimplify.emit_operation)

File pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py

         # not obvious, because of the exception UnicodeDecodeError that
         # can be raised by ll_str2unicode()
 
+    def test_record_known_class(self):
+        ops = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        record_known_class(p1, ConstClass(node_vtable))
+        guard_class(p1, ConstClass(node_vtable)) []
+        jump(p1)
+        """
+        expected = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        jump(p1)
+        """
+        self.optimize_loop(ops, expected)
+
     def test_quasi_immut(self):
         ops = """
         [p0, p1, i0]

File pypy/jit/metainterp/pyjitpl.py

     def opimpl_mark_opaque_ptr(self, box):
         return self.execute(rop.MARK_OPAQUE_PTR, box)
 
+    @arguments("box", "box")
+    def opimpl_record_known_class(self, box, clsbox):
+        from pypy.rpython.lltypesystem import llmemory
+        if self.metainterp.heapcache.is_class_known(box):
+            return
+        adr = clsbox.getaddr()
+        bounding_class = llmemory.cast_adr_to_ptr(adr, rclass.CLASSTYPE)
+        if bounding_class.subclassrange_max - bounding_class.subclassrange_min == 1:
+            # precise class knowledge, this can be used
+            self.execute(rop.RECORD_KNOWN_CLASS, box, clsbox)
+            self.metainterp.heapcache.class_now_known(box)
+
     @arguments("box")
     def _opimpl_any_return(self, box):
         self.metainterp.finishframe(box)

File pypy/jit/metainterp/resoperation.py

     'COPYSTRCONTENT/5',       # src, dst, srcstart, dststart, length
     'COPYUNICODECONTENT/5',
     'QUASIIMMUT_FIELD/1d',    # [objptr], descr=SlowMutateDescr
+    'RECORD_KNOWN_CLASS/2',   # [objptr, clsptr]
 
     '_CANRAISE_FIRST', # ----- start of can_raise operations -----
     '_CALL_FIRST',

File pypy/jit/metainterp/test/test_ajit.py

         self.interp_operations(f, [5], translationoptions=translationoptions)
 
 
+    def test_annotation_gives_knowledge_to_tracer(self):
+        class Base(object):
+            pass
+        class A(Base):
+            def f(self):
+                return self.a
+            def g(self):
+                return self.a + 1
+        class B(Base):
+            def f(self):
+                return self.b
+            def g(self):
+                return self.b + 1
+        class C(B):
+            def f(self):
+                self.c += 1
+                return self.c
+            def g(self):
+                return self.c + 1
+        @dont_look_inside
+        def make(x):
+            if x > 0:
+                a = A()
+                a.a = x + 1
+            elif x < 0:
+                a = B()
+                a.b = -x
+            else:
+                a = C()
+                a.c = 10
+            return a
+        def f(x):
+            a = make(x)
+            if x > 0:
+                assert isinstance(a, A)
+                z = a.f()
+            elif x < 0:
+                assert isinstance(a, B)
+                z = a.f()
+            else:
+                assert isinstance(a, C)
+                z = a.f()
+            return z + a.g()
+        res1 = f(6)
+        res2 = self.interp_operations(f, [6])
+        assert res1 == res2
+        self.check_operations_history(guard_class=0, record_known_class=1)
+
+        res1 = f(-6)
+        res2 = self.interp_operations(f, [-6])
+        assert res1 == res2
+        # cannot use record_known_class here, because B has a subclass
+        self.check_operations_history(guard_class=1)
+
+        res1 = f(0)
+        res2 = self.interp_operations(f, [0])
+        assert res1 == res2
+        # here it works again
+        self.check_operations_history(guard_class=0, record_known_class=1)
+
+
 class TestLLtype(BaseLLtypeTests, LLJitMixin):
     def test_tagged(self):
         from pypy.rlib.objectmodel import UnboxedValue