[pypy-commit] pypy default: Tweak the logic for ll_isinstance(): in the common case of no

arigo noreply at buildbot.pypy.org
Sun Nov 23 23:54:23 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r74654:c286dc658327
Date: 2014-11-23 23:54 +0100
http://bitbucket.org/pypy/pypy/changeset/c286dc658327/

Log:	Tweak the logic for ll_isinstance(): in the common case of no
	subclasses, improve the performance (a little bit). Fixes an age-
	old "XXX re-implement the following optimization".

diff --git a/rpython/rtyper/normalizecalls.py b/rpython/rtyper/normalizecalls.py
--- a/rpython/rtyper/normalizecalls.py
+++ b/rpython/rtyper/normalizecalls.py
@@ -298,12 +298,16 @@
 
 # ____________________________________________________________
 
+class TooLateForNewSubclass(Exception):
+    pass
+
 class TotalOrderSymbolic(ComputedIntSymbolic):
 
     def __init__(self, orderwitness, peers):
         self.orderwitness = orderwitness
         self.peers = peers
         self.value = None
+        self._with_subclasses = None    # unknown
         peers.append(self)
 
     def __cmp__(self, other):
@@ -320,12 +324,34 @@
     def __rsub__(self, other):
         return other - self.compute_fn()
 
+    def check_any_subclass_in_peer_list(self, i):
+        # check if the next peer, in order, is or not the end
+        # marker for this start marker
+        assert self.peers[i] is self
+        return self.peers[i + 1].orderwitness != self.orderwitness + [MAX]
+
+    def number_with_subclasses(self):
+        # Return True or False depending on whether this is the
+        # subclassrange_min corresponding to a class which has subclasses
+        # or not.  If this is called and returns False, then adding later
+        # new subclasses will crash in compute_fn().
+        if self._with_subclasses is None:     # unknown so far
+            self.peers.sort()
+            i = self.peers.index(self)
+            self._with_subclasses = self.check_any_subclass_in_peer_list(i)
+        return self._with_subclasses
+
     def compute_fn(self):
         if self.value is None:
             self.peers.sort()
             for i, peer in enumerate(self.peers):
                 assert peer.value is None or peer.value == i
                 peer.value = i
+                #
+                if peer._with_subclasses is False:
+                    if peer.check_any_subclass_in_peer_list(i):
+                        raise TooLateForNewSubclass
+                #
             assert self.value is not None
         return self.value
 
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -1007,14 +1007,11 @@
         v_obj, v_cls = hop.inputargs(instance_repr, class_repr)
         if isinstance(v_cls, Constant):
             cls = v_cls.value
-            # XXX re-implement the following optimization
-            #if cls.subclassrange_max == cls.subclassrange_min:
-            #    # a class with no subclass
-            #    return hop.gendirectcall(rclass.ll_isinstance_exact, v_obj, v_cls)
-            #else:
-            minid = hop.inputconst(Signed, cls.subclassrange_min)
-            maxid = hop.inputconst(Signed, cls.subclassrange_max)
-            return hop.gendirectcall(ll_isinstance_const, v_obj, minid, maxid)
+            llf, llf_nonnull = make_ll_isinstance(self.rtyper, cls)
+            if hop.args_s[0].can_be_None:
+                return hop.gendirectcall(llf, v_obj)
+            else:
+                return hop.gendirectcall(llf_nonnull, v_obj)
         else:
             return hop.gendirectcall(ll_isinstance, v_obj, v_cls)
 
@@ -1128,16 +1125,26 @@
     obj_cls = obj.typeptr
     return ll_issubclass(obj_cls, cls)
 
-def ll_isinstance_const(obj, minid, maxid):
-    if not obj:
-        return False
-    return ll_issubclass_const(obj.typeptr, minid, maxid)
-
-def ll_isinstance_exact(obj, cls):
-    if not obj:
-        return False
-    obj_cls = obj.typeptr
-    return obj_cls == cls
+def make_ll_isinstance(rtyper, cls):
+    try:
+        return rtyper.isinstance_helpers[cls._obj]
+    except KeyError:
+        minid = cls.subclassrange_min
+        maxid = cls.subclassrange_max
+        if minid.number_with_subclasses():
+            def ll_isinstance_const_nonnull(obj):
+                objid = obj.typeptr.subclassrange_min
+                return llop.int_between(Bool, minid, objid, maxid)
+        else:
+            def ll_isinstance_const_nonnull(obj):
+                return obj.typeptr == cls
+        def ll_isinstance_const(obj):
+            if not obj:
+                return False
+            return ll_isinstance_const_nonnull(obj)
+        result = (ll_isinstance_const, ll_isinstance_const_nonnull)
+        rtyper.isinstance_helpers[cls._obj] = result
+        return result
 
 def ll_runtime_type_info(obj):
     return obj.typeptr.rtti
diff --git a/rpython/rtyper/rtyper.py b/rpython/rtyper/rtyper.py
--- a/rpython/rtyper/rtyper.py
+++ b/rpython/rtyper/rtyper.py
@@ -59,6 +59,7 @@
         self.typererror_count = 0
         # make the primitive_to_repr constant mapping
         self.primitive_to_repr = {}
+        self.isinstance_helpers = {}
         self.exceptiondata = ExceptionData(self)
         self.custom_trace_funcs = []
 
diff --git a/rpython/rtyper/test/test_normalizecalls.py b/rpython/rtyper/test/test_normalizecalls.py
--- a/rpython/rtyper/test/test_normalizecalls.py
+++ b/rpython/rtyper/test/test_normalizecalls.py
@@ -6,6 +6,7 @@
 from rpython.rtyper.test.test_llinterp import interpret
 from rpython.rtyper.lltypesystem import lltype
 from rpython.rtyper.normalizecalls import TotalOrderSymbolic, MAX
+from rpython.rtyper.normalizecalls import TooLateForNewSubclass
 
 
 def test_TotalOrderSymbolic():
@@ -21,6 +22,49 @@
     assert t1 <= 5
     assert t1.value == 0
 
+def test_TotalOrderSymbolic_with_subclasses():
+    lst = []
+    t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
+    t1 = TotalOrderSymbolic([3, 4], lst)
+    t2 = TotalOrderSymbolic([3, 4, 2], lst)
+    t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+    assert t1.number_with_subclasses()
+    assert not t2.number_with_subclasses()
+    assert [t.compute_fn() for t in [t1, t2, t3, t4]] == range(4)
+    #
+    lst = []
+    t1 = TotalOrderSymbolic([3, 4], lst)
+    t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
+    t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+    t2 = TotalOrderSymbolic([3, 4, 2], lst)
+    assert not t2.number_with_subclasses()
+    assert t1.number_with_subclasses()
+    assert [t.compute_fn() for t in [t1, t2, t3, t4]] == range(4)
+    #
+    lst = []
+    t1 = TotalOrderSymbolic([3, 4], lst)
+    t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+    assert not t1.number_with_subclasses()
+    t2 = TotalOrderSymbolic([3, 4, 2], lst)
+    t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
+    py.test.raises(TooLateForNewSubclass, t2.compute_fn)
+    #
+    lst = []
+    t1 = TotalOrderSymbolic([3, 4], lst)
+    t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+    assert not t1.number_with_subclasses()
+    t2 = TotalOrderSymbolic([1], lst)
+    t3 = TotalOrderSymbolic([1, MAX], lst)
+    assert [t.compute_fn() for t in [t2, t3, t1, t4]] == range(4)
+    #
+    lst = []
+    t1 = TotalOrderSymbolic([3, 4], lst)
+    t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+    assert not t1.number_with_subclasses()
+    t2 = TotalOrderSymbolic([6], lst)
+    t3 = TotalOrderSymbolic([6, MAX], lst)
+    assert [t.compute_fn() for t in [t1, t4, t2, t3]] == range(4)
+
 # ____________________________________________________________
 
 class TestNormalize(object):


More information about the pypy-commit mailing list