[pypy-svn] r6928 - in pypy/trunk/src/pypy: objspace/flow translator translator/test

arigo at codespeak.net arigo at codespeak.net
Thu Oct 14 16:14:15 CEST 2004


Author: arigo
Date: Thu Oct 14 16:14:14 2004
New Revision: 6928

Modified:
   pypy/trunk/src/pypy/objspace/flow/flowcontext.py
   pypy/trunk/src/pypy/objspace/flow/model.py
   pypy/trunk/src/pypy/objspace/flow/objspace.py
   pypy/trunk/src/pypy/translator/genc.h
   pypy/trunk/src/pypy/translator/genc.py
   pypy/trunk/src/pypy/translator/gencl.py
   pypy/trunk/src/pypy/translator/simplify.py
   pypy/trunk/src/pypy/translator/test/test_ctrans.py
Log:
Support for exceptions in genc.py.

* the objspace.flow.model is simplified for exceptions, see documentation
  of 'last_exception' in that module.

* fixed gencl.py to support the new model, too.

* new test for genc.py with 'for' loops and iterators.



Modified: pypy/trunk/src/pypy/objspace/flow/flowcontext.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/flowcontext.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/flowcontext.py	Thu Oct 14 16:14:14 2004
@@ -171,6 +171,10 @@
         raise AssertionError, "concrete mode: cannot guessbool(%s)" % (
             w_condition,)
 
+    def guessexception(self, *classes):
+        return self.guessbool(Constant(last_exception),
+                              cases = [None] + list(classes))
+
     def build_flow(self):
         while self.pendingblocks:
             block = self.pendingblocks.pop(0)

Modified: pypy/trunk/src/pypy/objspace/flow/model.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/model.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/model.py	Thu Oct 14 16:14:14 2004
@@ -50,7 +50,8 @@
     def __init__(self, inputargs):
         self.inputargs = list(inputargs)  # mixed list of variable/const 
         self.operations = []              # list of SpaceOperation(s)
-        self.exitswitch = None            # variable
+        self.exitswitch = None            # a variable or
+                                          #  Constant(last_exception), see below
         self.exits      = []              # list of Link(s)
 
     def getvariables(self):
@@ -148,6 +149,15 @@
     def __repr__(self):
         return "%r = %s(%s)" % (self.result, self.opname, ", ".join(map(repr, self.args)))
 
+class LastExceptionValue:
+    def __repr__(self):
+        return 'last_exception'
+last_exception = LastExceptionValue()
+# if Block().exitswitch == Constant(last_exception), it means that we are
+# interested in catching the exception that the *last operation* of the
+# block could raise.  The exitcases of the links are None for no exception
+# or XxxError classes to catch the matching exceptions.
+
 def uniqueitems(lst):
     "Returns a list with duplicate elements removed."
     result = []
@@ -241,30 +251,40 @@
 
         vars_previous_blocks = {}
 
-        def visit(node):
-            if isinstance(node, Block):
-                assert bool(node.isstartblock) == (node is graph.startblock)
-                if not node.exits:
-                    assert node in exitblocks
+        def visit(block):
+            if isinstance(block, Block):
+                assert bool(block.isstartblock) == (block is graph.startblock)
+                if not block.exits:
+                    assert block in exitblocks
                 vars = {}
-                for v in node.inputargs + [op.result for op in node.operations]:
+                resultvars = [op.result for op in block.operations]
+                for v in block.inputargs + resultvars:
                     assert isinstance(v, Variable)
                     assert v not in vars, "duplicate variable %r" % (v,)
                     assert v not in vars_previous_blocks, (
                         "variable %r used in more than one block" % (v,))
                     vars[v] = True
-                for op in node.operations:
+                for op in block.operations:
                     for v in op.args:
                         assert isinstance(v, (Constant, Variable))
                         if isinstance(v, Variable):
                             assert v in vars
-                if node.exitswitch is not None:
-                    assert isinstance(node.exitswitch, (Constant, Variable))
-                    if isinstance(node.exitswitch, Variable):
-                        assert node.exitswitch in vars
-                for link in node.exits:
+                if block.exitswitch is None:
+                    assert len(block.exits) <= 1
+                    if block.exits:
+                        assert block.exits[0].exitcase is None
+                elif block.exitswitch == Constant(last_exception):
+                    assert len(block.operations) >= 1
+                    assert len(block.exits) >= 1
+                    assert block.exits[0].exitcase is None
+                    for link in block.exits[1:]:
+                        assert issubclass(link.exitcase, Exception)
+                else:
+                    assert isinstance(block.exitswitch, Variable)
+                    assert block.exitswitch in vars
+                for link in block.exits:
                     assert len(link.args) == len(link.target.inputargs)
-                    assert link.prevblock is node
+                    assert link.prevblock is block
                     for v in link.args:
                         assert isinstance(v, (Constant, Variable))
                         if isinstance(v, Variable):

Modified: pypy/trunk/src/pypy/objspace/flow/objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/objspace.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/objspace.py	Thu Oct 14 16:14:14 2004
@@ -158,9 +158,8 @@
 
     def next(self, w_iter):
         w_item = self.do_operation("next", w_iter)
-        w_curexc = self.do_operation('exception', w_item)
         context = self.getexecutioncontext()
-        outcome = context.guessbool(w_curexc, [None, StopIteration])
+        outcome = context.guessexception(StopIteration)
         if outcome is StopIteration:
             raise OperationError(self.w_StopIteration, self.w_None)
         else:
@@ -185,10 +184,6 @@
 implicit_exceptions = {
     'getitem': [IndexError],
     }
-class ImplicitExcValue:
-    def __repr__(self):
-        return 'implicitexc'
-implicitexc = ImplicitExcValue()
 
 def extract_cell_content(c):
     """Get the value contained in a CPython 'cell', as read through
@@ -245,17 +240,18 @@
         #print >> sys.stderr, 'Variable operation', name, args_w
         w_result = self.do_operation(name, *args_w)
         if exceptions:
-            # the 'exception(w_result)' operation is a bit strange, it is
-            # meant to check if w_result is a correct result or if its
-            # computation actually resulted in an exception.  For now this
-            # is an approximation of checking if w_result is NULL, and
-            # using PyErr_Occurred() to get the current exception if so.
-            w_curexc = self.do_operation('exception', w_result)
+            # catch possible exceptions implicitely.  If the OperationError
+            # below is not caught in the same function, it will produce an
+            # exception-raising return block in the flow graph.  The special
+            # value 'wrap(last_exception)' is used as a marker for this kind
+            # of implicit exceptions, and simplify.py will remove it as per
+            # the RPython definition: implicit exceptions not explicitely
+            # caught in the same function are assumed not to occur.
             context = self.getexecutioncontext()
-            outcome = context.guessbool(w_curexc, [None] + exceptions)
+            outcome = context.guessexception(*exceptions)
             if outcome is not None:
                 raise OperationError(self.wrap(outcome),
-                                     self.wrap(implicitexc))
+                                     self.wrap(last_exception))
         return w_result
 
     setattr(FlowObjSpace, name, generic_operator)

Modified: pypy/trunk/src/pypy/translator/genc.h
==============================================================================
--- pypy/trunk/src/pypy/translator/genc.h	(original)
+++ pypy/trunk/src/pypy/translator/genc.h	Thu Oct 14 16:14:14 2004
@@ -79,6 +79,12 @@
 
 #define OP_NEWSLICE(x,y,z,r,err)  if (!(r=PySlice_New(x,y,z)))       goto err;
 
+#define OP_ITER(x,r,err)          if (!(r=PyObject_GetIter(x)))      goto err;
+#define OP_NEXT(x,r,err)          if (!(r=PyIter_Next(x))) {                   \
+		if (!PyErr_Occurred()) PyErr_SetNone(PyExc_StopIteration);     \
+		goto err;                                                      \
+	}
+
 
 /*** tests ***/
 
@@ -90,9 +96,6 @@
 
 /*** misc ***/
 
-  /* XXX exceptions not implemented */
-#define OP_EXCEPTION(x,r,err)  r=Py_None; Py_INCREF(r);
-
 #define MOVE(x, y)             y = x;
 
 #define INITCHK(expr)          if (!(expr)) return;

Modified: pypy/trunk/src/pypy/translator/genc.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc.py	(original)
+++ pypy/trunk/src/pypy/translator/genc.py	Thu Oct 14 16:14:14 2004
@@ -5,7 +5,7 @@
 from __future__ import generators
 import autopath, os
 from pypy.objspace.flow.model import Variable, Constant, SpaceOperation
-from pypy.objspace.flow.model import FunctionGraph, Block, Link
+from pypy.objspace.flow.model import FunctionGraph, Block, Link, last_exception
 from pypy.objspace.flow.model import traverse, uniqueitems, checkgraph
 from pypy.translator.simplify import remove_direct_loops
 
@@ -217,10 +217,15 @@
         # print the body
         for line in body:
             if line.endswith(':'):
-                line = '    ' + line
+                if line.startswith('err'):
+                    fmt = '\t%s'
+                else:
+                    fmt = '    %s\n'
+            elif line:
+                fmt = '\t%s\n'
             else:
-                line = '\t' + line
-            print >> f, line
+                fmt = '%s\n'
+            f.write(fmt % line)
         print >> f, '}'
 
         # print the PyMethodDef
@@ -289,10 +294,48 @@
                     yield '%s(%s)' % (macro, ', '.join(lst))
                 to_release.append(op.result)
 
+            err_reachable = False
             if len(block.exits) == 0:
-                yield 'return %s;' % expr(block.inputargs[0])
+                retval = expr(block.inputargs[0])
+                if hasattr(block, 'exc_type'):
+                    # exceptional return block
+                    yield 'PyErr_SetObject(PyExc_%s, %s);' % (
+                        block.exc_type.__name__, retval)
+                    yield 'return NULL;'
+                else:
+                    # regular return block
+                    yield 'return %s;' % retval
                 continue
-            if len(block.exits) > 1:
+            elif block.exitswitch is None:
+                # single-exit block
+                assert len(block.exits) == 1
+                for op in gen_link(block.exits[0]):
+                    yield op
+                yield ''
+            elif block.exitswitch == Constant(last_exception):
+                # block catching the exceptions raised by its last operation
+                # we handle the non-exceptional case first
+                link = block.exits[0]
+                assert link.exitcase is None
+                for op in gen_link(link):
+                    yield op
+                # we must catch the exception raised by the last operation,
+                # which goes to the last err%d_%d label written above.
+                yield ''
+                to_release.pop()  # skip default error handling for this label
+                yield 'err%d_%d:' % (blocknum[block], len(to_release))
+                yield ''
+                for link in block.exits[1:]:
+                    assert issubclass(link.exitcase, Exception)
+                    yield 'if (PyErr_ExceptionMatches(PyExc_%s)) {' % (
+                        link.exitcase.__name__,)
+                    yield '\tPyErr_Clear();'
+                    for op in gen_link(link):
+                        yield '\t' + op
+                    yield '}'
+                err_reachable = True
+            else:
+                # block ending in a switch on a value
                 for link in block.exits[:-1]:
                     yield 'if (EQ_%s(%s)) {' % (link.exitcase,
                                                 block.exitswitch.name)
@@ -302,16 +345,18 @@
                 link = block.exits[-1]
                 yield 'assert(EQ_%s(%s));' % (link.exitcase,
                                               block.exitswitch.name)
-            for op in gen_link(block.exits[-1]):
-                yield op
+                for op in gen_link(block.exits[-1]):
+                    yield op
+                yield ''
 
-            yield ''
-            to_release.pop()  # this label is never reachable
             while to_release:
-                n = len(to_release)
                 v = to_release.pop()
-                yield 'err%d_%d: Py_DECREF(%s);' % (blocknum[block], n, v.name)
-            yield 'err%d_0: return NULL;' % blocknum[block]
+                if err_reachable:
+                    yield 'Py_DECREF(%s);' % v.name
+                yield 'err%d_%d:' % (blocknum[block], len(to_release))
+                err_reachable = True
+            if err_reachable:
+                yield 'return NULL;'
 
 # ____________________________________________________________
 

Modified: pypy/trunk/src/pypy/translator/gencl.py
==============================================================================
--- pypy/trunk/src/pypy/translator/gencl.py	(original)
+++ pypy/trunk/src/pypy/translator/gencl.py	Thu Oct 14 16:14:14 2004
@@ -1,5 +1,4 @@
 from pypy.objspace.flow.model import *
-from pypy.objspace.flow.objspace import implicitexc
 from pypy.translator.annrpython import RPythonAnnotator
 
 from pypy.translator.simplify import simplify_graph
@@ -124,9 +123,6 @@
         print "(let ((result (funcall", s(iterator), ")))"
         print "  (setq", s(result), "(car result))"
         print "  (setq last-exc (cdr result)))"
-    def op_exception(self):
-        s = self.str
-        print "(psetq", s(self.result), "last-exc last-exc nil)"
     builtin_map = {
         pow: "expt",
         range: "python-range",
@@ -191,8 +187,8 @@
             return val
         elif isinstance(val, type(Exception)) and issubclass(val, Exception):
             return "'%s" % val.__name__
-        elif val is implicitexc:
-            return "'implicitexc"
+        elif val is last_exception:
+            return "last-exc"
         else:
             return "#<%r>" % (val,)
     def emitcode(self):

Modified: pypy/trunk/src/pypy/translator/simplify.py
==============================================================================
--- pypy/trunk/src/pypy/translator/simplify.py	(original)
+++ pypy/trunk/src/pypy/translator/simplify.py	Thu Oct 14 16:14:14 2004
@@ -2,7 +2,6 @@
 """
 
 from pypy.objspace.flow.model import *
-from pypy.objspace.flow.objspace import implicitexc
 
 def eliminate_empty_blocks(graph):
     """Eliminate basic blocks that do not contain any operations.
@@ -62,15 +61,17 @@
     traverse(visit, graph)
 
 def remove_implicit_exceptions(graph):
-    """An exception that is marked implicit (see implicitexc) and not
-    caught in the block is entierely removed.  This gets rid for example
-    of possible ValueErrors upon tuple unpacking, assuming they cannot
-    happen unless there is an exception handler in the same function."""
+    """An exception raised implicitely has a particular value of
+    space.wrap(last_exception) -- see pypy.objspace.flow.objspace.make_op --
+    which shows up in the flow graph if the exception is not caught.  This
+    function removes such exceptions entierely.  This gets rid for example
+    of possible IndexErrors by 'getitem', assuming they cannot happen unless
+    there is an exception handler in the same function."""
     def visit(link):
         if isinstance(link, Link) and link in link.prevblock.exits:
             if (isinstance(link.exitcase, type(Exception)) and
                 issubclass(link.exitcase, Exception) and
-                link.args == [Constant(implicitexc)] and
+                link.args == [Constant(last_exception)] and
                 len(link.target.exits) == 0 and
                 hasattr(link.target, 'exc_type')):
                 # remove direct links to implicit exception return blocks

Modified: pypy/trunk/src/pypy/translator/test/test_ctrans.py
==============================================================================
--- pypy/trunk/src/pypy/translator/test/test_ctrans.py	(original)
+++ pypy/trunk/src/pypy/translator/test/test_ctrans.py	Thu Oct 14 16:14:14 2004
@@ -84,6 +84,11 @@
         self.assertEquals(sand(0, 6), "no")
         self.assertEquals(sand(0, 0), "no")
 
+    def test_yast(self):
+        yast = self.build_cfunc(snippet.yast)
+        self.assertEquals(yast([1000,100,10,1]), 1111)
+        self.assertEquals(yast(range(100)), (99*100)/2)
+
 class TypedTestCase(testit.IntTestCase):
 
     def getcompiled(self, func):



More information about the Pypy-commit mailing list