[pypy-svn] r36008 - in pypy/dist/pypy: annotation objspace/flow/test rpython rpython/lltypesystem rpython/ootypesystem rpython/test translator

pedronis at codespeak.net pedronis at codespeak.net
Wed Dec 27 22:23:21 CET 2006


Author: pedronis
Date: Wed Dec 27 22:23:16 2006
New Revision: 36008

Modified:
   pypy/dist/pypy/annotation/annrpython.py
   pypy/dist/pypy/annotation/binaryop.py
   pypy/dist/pypy/annotation/model.py
   pypy/dist/pypy/objspace/flow/test/test_objspace.py
   pypy/dist/pypy/rpython/lltypesystem/rstr.py
   pypy/dist/pypy/rpython/ootypesystem/ootype.py
   pypy/dist/pypy/rpython/rlist.py
   pypy/dist/pypy/rpython/rmodel.py
   pypy/dist/pypy/rpython/rstr.py
   pypy/dist/pypy/rpython/test/test_llinterp.py
   pypy/dist/pypy/rpython/test/test_rlist.py
   pypy/dist/pypy/rpython/test/test_rstr.py
   pypy/dist/pypy/translator/geninterplevel.py
   pypy/dist/pypy/translator/simplify.py
Log:
Better precision for when to generate bound checking versions of getitem.
Have a transformation that rename getitem into getitem(_idx)(_key) based on whether the getitem is effectively
guarded by such exception (IndexError, KeyError) exits.

Change annotation and rtyping to work accordingly.

Done with more precision for lists and strings for now, fallbacks in place for the rest.
(Should be done for setitem too)

Tests for the transformation in objspace/flow and for the final behavior in the rpython/test.



Modified: pypy/dist/pypy/annotation/annrpython.py
==============================================================================
--- pypy/dist/pypy/annotation/annrpython.py	(original)
+++ pypy/dist/pypy/annotation/annrpython.py	Wed Dec 27 22:23:16 2006
@@ -591,11 +591,11 @@
                 arg1 = self.binding(op.args[0])
                 arg2 = self.binding(op.args[1])
                 binop = getattr(pair(arg1, arg2), op.opname, None)
-                can_only_throw = read_can_only_throw(binop, arg1, arg2)
+                can_only_throw = annmodel.read_can_only_throw(binop, arg1, arg2)
             elif op.opname in annmodel.UNARY_OPERATIONS:
                 arg1 = self.binding(op.args[0])
                 unop = getattr(arg1, op.opname, None)
-                can_only_throw = read_can_only_throw(unop, arg1)
+                can_only_throw = annmodel.read_can_only_throw(unop, arg1)
             else:
                 can_only_throw = None
 
@@ -814,9 +814,3 @@
         return "<BlockedInference break_at %s [%s]>" %(break_at, self.op)
 
     __str__ = __repr__
-
-def read_can_only_throw(opimpl, *args):
-    can_only_throw = getattr(opimpl, "can_only_throw", None)
-    if can_only_throw is None or isinstance(can_only_throw, list):
-        return can_only_throw
-    return can_only_throw(*args)

Modified: pypy/dist/pypy/annotation/binaryop.py
==============================================================================
--- pypy/dist/pypy/annotation/binaryop.py	(original)
+++ pypy/dist/pypy/annotation/binaryop.py	Wed Dec 27 22:23:16 2006
@@ -16,6 +16,7 @@
 from pypy.annotation.model import SomeWeakGcAddress
 from pypy.annotation.model import SomeCTypesObject
 from pypy.annotation.model import unionof, UnionError, set, missing_operation, TLS
+from pypy.annotation.model import read_can_only_throw
 from pypy.annotation.model import add_knowntypedata, merge_knowntypedata
 from pypy.annotation.model import lltype_to_annotation
 from pypy.annotation.bookkeeper import getbookkeeper
@@ -34,6 +35,7 @@
                          'and_', 'or_', 'xor',
                          'lshift', 'rshift',
                          'getitem', 'setitem', 'delitem',
+                         'getitem_idx', 'getitem_key', 'getitem_idx_key',
                          'inplace_add', 'inplace_sub', 'inplace_mul',
                          'inplace_truediv', 'inplace_floordiv', 'inplace_div',
                          'inplace_mod', 'inplace_pow',
@@ -214,6 +216,20 @@
         else:
             return obj
 
+    # checked getitems
+
+    def _getitem_can_only_throw(s_c1, s_o2):
+        impl = pair(s_c1, s_o2).getitem
+        return read_can_only_throw(impl, s_c1, s_o2)
+
+    def getitem_idx_key((s_c1, s_o2)):
+        impl = pair(s_c1, s_o2).getitem
+        return impl()
+    getitem_idx_key.can_only_throw = _getitem_can_only_throw
+
+    getitem_idx = getitem_idx_key
+    getitem_key = getitem_idx_key
+        
 
 # cloning a function with identical code, for the can_only_throw attribute
 def _clone(f, can_only_throw = None):
@@ -512,7 +528,16 @@
     def getitem((lst1, int2)):
         getbookkeeper().count("list_getitem", int2)
         return lst1.listdef.read_item()
-    getitem.can_only_throw = [IndexError]
+    getitem.can_only_throw = []
+
+    getitem_key = getitem
+
+    def getitem_idx((lst1, int2)):
+        getbookkeeper().count("list_getitem", int2)
+        return lst1.listdef.read_item()
+    getitem_idx.can_only_throw = [IndexError]
+
+    getitem_idx_key = getitem_idx
 
     def setitem((lst1, int2), s_value):
         getbookkeeper().count("list_setitem", int2)        
@@ -552,7 +577,16 @@
     def getitem((str1, int2)):
         getbookkeeper().count("str_getitem", int2)        
         return SomeChar()
-    getitem.can_only_throw = [IndexError]
+    getitem.can_only_throw = []
+
+    getitem_key = getitem
+
+    def getitem_idx((str1, int2)):
+        getbookkeeper().count("str_getitem", int2)        
+        return SomeChar()        
+    getitem_idx.can_only_throw = [IndexError]
+
+    getitem_idx_key = getitem_idx
 
     def mul((str1, int2)): # xxx do we want to support this
         getbookkeeper().count("str_mul", str1, int2)

Modified: pypy/dist/pypy/annotation/model.py
==============================================================================
--- pypy/dist/pypy/annotation/model.py	(original)
+++ pypy/dist/pypy/annotation/model.py	Wed Dec 27 22:23:16 2006
@@ -718,6 +718,13 @@
     BlockedInference: the current block is blocked, but not in a way
     that gives 'Blocked block' errors at the end of annotation."""
 
+
+def read_can_only_throw(opimpl, *args):
+    can_only_throw = getattr(opimpl, "can_only_throw", None)
+    if can_only_throw is None or isinstance(can_only_throw, list):
+        return can_only_throw
+    return can_only_throw(*args)
+
 #
 # safety check that no-one is trying to make annotation and translation
 # faster by providing the -O option to Python.

Modified: pypy/dist/pypy/objspace/flow/test/test_objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/test/test_objspace.py	(original)
+++ pypy/dist/pypy/objspace/flow/test/test_objspace.py	Wed Dec 27 22:23:16 2006
@@ -711,6 +711,90 @@
             return x[s]
         graph = self.codetest(myfunc)
 
+    def test_getitem(self):
+        def f(c, x):
+            try:
+                return c[x]
+            except Exception:
+                raise
+        graph = self.codetest(f)
+        simplify_graph(graph)
+        assert self.all_operations(graph) == {'getitem_idx_key': 1}
+
+        g = lambda: None
+        def f(c, x):
+            try:
+                return c[x]
+            finally:
+                g()
+        graph = self.codetest(f)
+        simplify_graph(graph)
+        assert self.all_operations(graph) == {'getitem_idx_key': 1,
+                                              'simple_call': 2}
+
+        def f(c, x):
+            try:
+                return c[x]
+            except IndexError:
+                raise
+        graph = self.codetest(f)
+        simplify_graph(graph)
+        assert self.all_operations(graph) == {'getitem_idx': 1}        
+
+        def f(c, x):
+            try:
+                return c[x]
+            except KeyError:
+                raise
+        graph = self.codetest(f)
+        simplify_graph(graph)
+        assert self.all_operations(graph) == {'getitem_key': 1}
+        
+        def f(c, x):
+            try:
+                return c[x]
+            except ValueError:
+                raise
+        graph = self.codetest(f)
+        simplify_graph(graph)
+        assert self.all_operations(graph) == {'getitem': 1}
+
+        def f(c, x):
+            try:
+                return c[x]
+            except Exception:
+                return -1
+        graph = self.codetest(f)
+        simplify_graph(graph)
+        self.show(graph)
+        assert self.all_operations(graph) == {'getitem_idx_key': 1}
+        
+        def f(c, x):
+            try:
+                return c[x]
+            except IndexError:
+                return -1
+        graph = self.codetest(f)
+        simplify_graph(graph)
+        assert self.all_operations(graph) == {'getitem_idx': 1}
+
+        def f(c, x):
+            try:
+                return c[x]
+            except KeyError:
+                return -1
+        graph = self.codetest(f)
+        simplify_graph(graph)
+        assert self.all_operations(graph) == {'getitem_key': 1}
+  
+        def f(c, x):
+            try:
+                return c[x]
+            except ValueError:
+                return -1
+        graph = self.codetest(f)
+        simplify_graph(graph)
+        assert self.all_operations(graph) == {'getitem': 1}
 
 class TestFlowObjSpaceDelay(Base):
     def setup_class(cls):

Modified: pypy/dist/pypy/rpython/lltypesystem/rstr.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rstr.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/rstr.py	Wed Dec 27 22:23:16 2006
@@ -160,7 +160,10 @@
         return len(s.chars)
 
     def ll_stritem_nonneg(s, i):
-        return s.chars[i]
+        chars = s.chars
+        debug_assert(i>=0, "negative str getitem index")
+        debug_assert(i<len(chars), "str getitem index out of bound")
+        return chars[i]
 
     def ll_chr2str(ch):
         s = mallocstr(1)

Modified: pypy/dist/pypy/rpython/ootypesystem/ootype.py
==============================================================================
--- pypy/dist/pypy/rpython/ootypesystem/ootype.py	(original)
+++ pypy/dist/pypy/rpython/ootypesystem/ootype.py	Wed Dec 27 22:23:16 2006
@@ -984,8 +984,9 @@
 
     def ll_stritem_nonneg(self, i):
         # NOT_RPYTHON
-        assert i >= 0
-        return self._str[i]
+        s = self._str
+        assert 0 <= i < len(s)
+        return s[i]
 
     def ll_strlen(self):
         # NOT_RPYTHON

Modified: pypy/dist/pypy/rpython/rlist.py
==============================================================================
--- pypy/dist/pypy/rpython/rlist.py	(original)
+++ pypy/dist/pypy/rpython/rlist.py	Wed Dec 27 22:23:16 2006
@@ -213,8 +213,8 @@
 
 class __extend__(pairtype(AbstractBaseListRepr, IntegerRepr)):
 
-    def rtype_getitem((r_lst, r_int), hop):
-        if hop.has_implicit_exception(IndexError):
+    def rtype_getitem((r_lst, r_int), hop, checkidx=False):
+        if checkidx:
             spec = dum_checkidx
         else:
             spec = dum_nocheck
@@ -224,10 +224,20 @@
             llfn = ll_getitem_nonneg
         else:
             llfn = ll_getitem
-        hop.exception_is_here()
+        if checkidx:
+            hop.exception_is_here()
+        else:
+            hop.exception_cannot_occur()
         v_res = hop.gendirectcall(llfn, v_func, v_lst, v_index)
         return r_lst.recast(hop.llops, v_res)
 
+    rtype_getitem_key = rtype_getitem
+
+    def rtype_getitem_idx((r_lst, r_int), hop):
+        return pair(r_lst, r_int).rtype_getitem(hop, checkidx=True)
+
+    rtype_getitem_idx_key = rtype_getitem_idx
+    
     def rtype_setitem((r_lst, r_int), hop):
         if hop.has_implicit_exception(IndexError):
             spec = dum_checkidx

Modified: pypy/dist/pypy/rpython/rmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/rmodel.py	(original)
+++ pypy/dist/pypy/rpython/rmodel.py	Wed Dec 27 22:23:16 2006
@@ -1,4 +1,4 @@
-from pypy.annotation.pairtype import pairtype, extendabletype
+from pypy.annotation.pairtype import pairtype, extendabletype, pair
 from pypy.annotation import model as annmodel
 from pypy.annotation import description
 from pypy.objspace.flow.model import Constant
@@ -296,6 +296,14 @@
             return inputconst(Bool, hop.s_result.const)
         return hop.rtyper.type_system.generic_is(robj1, robj2, hop)
 
+    # default implementation for checked getitems
+    
+    def rtype_getitem_idx_key((r_c1, r_o1), hop):
+        return pair(r_c1, r_o1).rtype_getitem(hop)
+
+    rtype_getitem_idx = rtype_getitem_idx_key
+    rtype_getitem_key = rtype_getitem_idx_key
+
 # ____________________________________________________________
 
 

Modified: pypy/dist/pypy/rpython/rstr.py
==============================================================================
--- pypy/dist/pypy/rpython/rstr.py	(original)
+++ pypy/dist/pypy/rpython/rstr.py	Wed Dec 27 22:23:16 2006
@@ -1,5 +1,5 @@
 from pypy.tool.staticmethods import StaticMethods
-from pypy.annotation.pairtype import pairtype
+from pypy.annotation.pairtype import pairtype, pair
 from pypy.annotation import model as annmodel
 from pypy.rpython.error import TyperError
 from pypy.rpython.rmodel import IntegerRepr, IteratorRepr
@@ -243,10 +243,10 @@
 
 
 class __extend__(pairtype(AbstractStringRepr, IntegerRepr)):
-    def rtype_getitem((r_str, r_int), hop):
+    def rtype_getitem((r_str, r_int), hop, checkidx=False):
         string_repr = hop.rtyper.type_system.rstr.string_repr
         v_str, v_index = hop.inputargs(string_repr, Signed)
-        if hop.has_implicit_exception(IndexError):
+        if checkidx:
             if hop.args_s[1].nonneg:
                 llfn = r_str.ll.ll_stritem_nonneg_checked
             else:
@@ -256,9 +256,19 @@
                 llfn = r_str.ll.ll_stritem_nonneg
             else:
                 llfn = r_str.ll.ll_stritem
-        hop.exception_is_here()
+        if checkidx:
+            hop.exception_is_here()
+        else:
+            hop.exception_cannot_occur()
         return hop.gendirectcall(llfn, v_str, v_index)
 
+    rtype_getitem_key = rtype_getitem
+
+    def rtype_getitem_idx((r_str, r_int), hop):
+        return pair(r_str, r_int).rtype_getitem(hop, checkidx=True)
+
+    rtype_getitem_idx_key = rtype_getitem_idx
+
 
 class __extend__(pairtype(AbstractStringRepr, AbstractSliceRepr)):
 

Modified: pypy/dist/pypy/rpython/test/test_llinterp.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_llinterp.py	(original)
+++ pypy/dist/pypy/rpython/test/test_llinterp.py	Wed Dec 27 22:23:16 2006
@@ -105,7 +105,11 @@
                                      someobjects, type_system=type_system,
                                      backendopt=backendopt)
     info = py.test.raises(LLException, "interp.eval_graph(graph, values)")
-    assert interp.find_exception(info.value) is exc, "wrong exception type"
+    try:
+        got = interp.find_exception(info.value)
+    except ValueError:
+        got = None
+    assert got is exc, "wrong exception type"
 
 #__________________________________________________________________
 # tests

Modified: pypy/dist/pypy/rpython/test/test_rlist.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rlist.py	(original)
+++ pypy/dist/pypy/rpython/test/test_rlist.py	Wed Dec 27 22:23:16 2006
@@ -1037,6 +1037,109 @@
         res = self.interpret(dummyfn, [1, 0])
         assert res == 0
 
+
+    def test_getitem_exc(self):
+        def f(x):
+            l = [1]
+            return l[x]
+
+        res = self.interpret(f, [0])
+        assert res == 1
+        py.test.raises(AssertionError, self.interpret, f, [1])
+
+        def f(x):
+            l = [1]
+            try:
+                return l[x]
+            except IndexError:
+                return -1
+            except Exception:
+                return 0
+
+        res = self.interpret(f, [0])
+        assert res == 1
+        res = self.interpret(f, [1])
+        assert res == -1        
+
+        def f(x):
+            l = [1]
+            try:
+                return l[x]
+            except Exception:
+                return 0
+
+        res = self.interpret(f, [0])
+        assert res == 1
+        res = self.interpret(f, [1])
+        assert res == 0
+
+        def f(x):
+            l = [1]
+            try:
+                return l[x]
+            except ValueError:
+                return 0
+
+        res = self.interpret(f, [0])
+        assert res == 1
+        
+    def test_getitem_exc(self):
+        def f(x):
+            l = [1]
+            return l[x]
+
+        res = self.interpret(f, [0])
+        assert res == 1
+        try:
+            self.interpret_raises(IndexError, f, [1])
+        except (AssertionError,), e:
+            pass
+        else:
+            assert False
+
+        def f(x):
+            l = [1]
+            try:
+                return l[x]
+            except IndexError:
+                return -1
+            except Exception:
+                return 0
+
+        res = self.interpret(f, [0])
+        assert res == 1
+        res = self.interpret(f, [1])
+        assert res == -1        
+
+        def f(x):
+            l = [1]
+            try:
+                return l[x]
+            except Exception:
+                return 0
+
+        res = self.interpret(f, [0])
+        assert res == 1
+        res = self.interpret(f, [1])
+        assert res == 0
+
+        def f(x):
+            l = [1]
+            try:
+                return l[x]
+            except ValueError:
+                return 0
+
+        res = self.interpret(f, [0])
+        assert res == 1
+        try:
+            self.interpret_raises(IndexError, f, [1])
+        except (AssertionError,), e:
+            pass
+        else:
+            assert False
+
+
 class TestLLtype(BaseTestRlist, LLRtypeMixin):
     rlist = ll_rlist
 

Modified: pypy/dist/pypy/rpython/test/test_rstr.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rstr.py	(original)
+++ pypy/dist/pypy/rpython/test/test_rstr.py	Wed Dec 27 22:23:16 2006
@@ -651,7 +651,62 @@
 
         res = self.interpret(f, [self.string_to_ll("abba")])
         assert res
-
+       
+    def test_getitem_exc(self):
+        def f(x):
+            s = "z"
+            return s[x]
+
+        res = self.interpret(f, [0])
+        assert res == 'z'
+        try:
+            self.interpret_raises(IndexError, f, [1])
+        except (AssertionError,), e:
+            pass
+        else:
+            assert False
+    
+        def f(x):
+            s = "z"
+            try:
+                return s[x]
+            except IndexError:
+                return 'X'
+            except Exception:
+                return ' '
+
+        res = self.interpret(f, [0])
+        assert res == 'z'
+        res = self.interpret(f, [1])
+        assert res == 'X'        
+
+        def f(x):
+            s = "z"
+            try:
+                return s[x]
+            except Exception:
+                return ' '
+
+        res = self.interpret(f, [0])
+        assert res == 'z'
+        res = self.interpret(f, [1])
+        assert res == ' '
+
+        def f(x):
+            s = "z"
+            try:
+                return s[x]
+            except ValueError:
+                return ' '
+
+        res = self.interpret(f, [0])
+        assert res == 'z'
+        try:
+            self.interpret_raises(IndexError, f, [1])
+        except (AssertionError,), e:
+            pass
+        else:
+            assert False
 
 def FIXME_test_str_to_pystringobj():
     def f(n):

Modified: pypy/dist/pypy/translator/geninterplevel.py
==============================================================================
--- pypy/dist/pypy/translator/geninterplevel.py	(original)
+++ pypy/dist/pypy/translator/geninterplevel.py	Wed Dec 27 22:23:16 2006
@@ -284,8 +284,11 @@
         else:
             fmt = "%s = %s(%s)"
         # special case is_true
-        wrapped = op.opname != "is_true"
-        oper = "space.%s" % op.opname
+        opname = op.opname
+        if opname.startswith('getitem_'):
+            opname = 'getitem'
+        wrapped = opname != "is_true"
+        oper = "space.%s" % opname
         return fmt % (self.expr(op.result, localscope, wrapped), oper,
                       self.arglist(op.args, localscope))
 

Modified: pypy/dist/pypy/translator/simplify.py
==============================================================================
--- pypy/dist/pypy/translator/simplify.py	(original)
+++ pypy/dist/pypy/translator/simplify.py	Wed Dec 27 22:23:16 2006
@@ -288,6 +288,22 @@
 
     traverse(visit, graph)
 
+def transform_xxxitem(graph):
+    # xxx setitem too
+    for block in graph.iterblocks():
+        if block.operations and block.exitswitch == c_last_exception:
+            last_op = block.operations[-1]
+            if last_op.opname == 'getitem':
+                postfx = []
+                for exit in block.exits:
+                    if exit.exitcase is IndexError:
+                        postfx.append('idx')
+                    elif exit.exitcase is KeyError:
+                        postfx.append('key')
+                if postfx:
+                    last_op.opname = last_op.opname + '_' + '_'.join(postfx)
+
+
 def remove_dead_exceptions(graph):
     """Exceptions can be removed if they are unreachable"""
 
@@ -1039,6 +1055,7 @@
     remove_identical_vars,
     transform_ovfcheck,
     simplify_exceptions,
+    transform_xxxitem,
     remove_dead_exceptions,
     ]
 



More information about the Pypy-commit mailing list