[Python-checkins] r64523 - in python/branches/tlee-ast-optimize: Lib/test/test_optimizer.py Python/optimize.c

thomas.lee python-checkins at python.org
Wed Jun 25 15:01:29 CEST 2008


Author: thomas.lee
Date: Wed Jun 25 15:01:28 2008
New Revision: 64523

Log:
Remove all code for eliminating unreachable branches ('continue' in 'finally' clauses need to raise syntax errors). Eliminate code optimizing read-only lists (until a decision about how to deal with 'in' and 'not in' work for non-hashable objects is made)

Modified:
   python/branches/tlee-ast-optimize/Lib/test/test_optimizer.py
   python/branches/tlee-ast-optimize/Python/optimize.c

Modified: python/branches/tlee-ast-optimize/Lib/test/test_optimizer.py
==============================================================================
--- python/branches/tlee-ast-optimize/Lib/test/test_optimizer.py	(original)
+++ python/branches/tlee-ast-optimize/Lib/test/test_optimizer.py	Wed Jun 25 15:01:28 2008
@@ -25,39 +25,39 @@
         self.assertEqual(2, ast.body[1].body[0].value.n)
         self.assertEqual(1, ast.body[1].orelse[0].value.n)
 
-    def test_fold_if_stmt_with_constants(self):
-        # ensure we can optimize conditionals using simple constants
-        code = """
-if 1:
-    'true'
-else:
-    'false'
-
-if 0:
-    'true'
-else:
-    'false'
-
-"""
-        ast = self.compileast(code)
-        self.assertEqual(2, len(ast.body))
-        self.assertEqual(_ast.Str, ast.body[0].value.__class__)
-        self.assertEqual('true', ast.body[0].value.s)
-        self.assertEqual(_ast.Str, ast.body[1].value.__class__)
-        self.assertEqual('false', ast.body[1].value.s)
-
-    def test_fold_unary_op_before_collapse_branch(self):
-        # ensure unary op folding is applied before collapsing a branch
-        code = """
-if not 1:
-    'true'
-else:
-    'false'
-"""
-        ast = self.compileast(code)
-        self.assertEqual(1, len(ast.body))
-        self.assertEqual(_ast.Str, ast.body[0].value.__class__)
-        self.assertEqual('false', ast.body[0].value.s)
+#    def test_fold_if_stmt_with_constants(self):
+#        # ensure we can optimize conditionals using simple constants
+#        code = """
+#if 1:
+#    'true'
+#else:
+#    'false'
+#
+#if 0:
+#    'true'
+#else:
+#    'false'
+#
+#"""
+#        ast = self.compileast(code)
+#        self.assertEqual(2, len(ast.body))
+#        self.assertEqual(_ast.Str, ast.body[0].value.__class__)
+#        self.assertEqual('true', ast.body[0].value.s)
+#        self.assertEqual(_ast.Str, ast.body[1].value.__class__)
+#        self.assertEqual('false', ast.body[1].value.s)
+
+#    def test_fold_unary_op_before_collapse_branch(self):
+#        # ensure unary op folding is applied before collapsing a branch
+#        code = """
+#if not 1:
+#    'true'
+#else:
+#    'false'
+#"""
+#        ast = self.compileast(code)
+#        self.assertEqual(1, len(ast.body))
+#        self.assertEqual(_ast.Str, ast.body[0].value.__class__)
+#        self.assertEqual('false', ast.body[0].value.s)
 
     def assertAstNode(self, expected_type, attr, expected_value, code):
         ast = self.compileast(code)
@@ -156,19 +156,19 @@
         except TypeError:
             pass
 
-    def test_eliminate_code_after_return(self):
-        # ensure code following a "return" is erased from the AST
-        code = """
-def say_hello():
-    print "Hello there"
-    return True
-    print "A secret message!"
-"""
-        ast = self.compileast(code)
-        self.assertEqual(1, len(ast.body))
-        self.assertEqual(_ast.FunctionDef, ast.body[0].__class__)
-        self.assertEqual(3, len(ast.body[0].body))
-        self.assertEqual(_ast.Pass, ast.body[0].body[2].__class__)
+#    def test_eliminate_code_after_return(self):
+#        # ensure code following a "return" is erased from the AST
+#        code = """
+#def say_hello():
+#    print "Hello there"
+#    return True
+#    print "A secret message!"
+#"""
+#        ast = self.compileast(code)
+#        self.assertEqual(1, len(ast.body))
+#        self.assertEqual(_ast.FunctionDef, ast.body[0].__class__)
+#        self.assertEqual(3, len(ast.body[0].body))
+#        self.assertEqual(_ast.Pass, ast.body[0].body[2].__class__)
 
     def test_yield_none_becomes_yield(self):
         code = """
@@ -195,16 +195,16 @@
         self.assertEqual(_ast.Return, ast.body[0].body[0].__class__)
         self.assertEqual(None, ast.body[0].body[0].value)
 
-    def test_generators_work_even_if_yields_are_optimized_away(self):
-        code = """
-def mygen():
-    return
-    yield 5
-"""
-
-        ast = self.compileast(code)
-        self.assertEqual(_ast.Return, ast.body[0].body[0].__class__)
-        self.assertEqual(_ast.Pass, ast.body[0].body[1].__class__)
+#    def test_generators_work_even_if_yields_are_optimized_away(self):
+#        code = """
+#def mygen():
+#    return
+#    yield 5
+#"""
+#
+#        ast = self.compileast(code)
+#        self.assertEqual(_ast.Return, ast.body[0].body[0].__class__)
+#        self.assertEqual(_ast.Pass, ast.body[0].body[1].__class__)
 
     def test_tuple_of_constants(self):
         tests = [
@@ -223,31 +223,31 @@
             self.assertEqual(tuple, ast.body[0].value.value.__class__)
             self.assertEqual(obj, ast.body[0].value.value)
 
-    def test_skip_unreachable_for_loop(self):
-        code = """
-for i in []:
-    print i
-"""
-        ast = self.compileast(code)
-        self.assertEqual(_ast.Pass, ast.body[0].__class__)
-
-    def test_skip_unreachable_while_loop(self):
-        code = """
-while 0:
-    print 'foo'
-"""
-
-        ast = self.compileast(code)
-        self.assertEqual(_ast.Pass, ast.body[0].__class__)
-
-    def test_fold_constant_list_in_for_loop(self):
-        code = """
-for i in [1, 2, 3]:
-    print i
-"""
-        ast = self.compileast(code)
-        self.assertEqual(_ast.Const, ast.body[0].iter.__class__)
-        self.assertEqual((1, 2, 3), ast.body[0].iter.value)
+#    def test_skip_unreachable_for_loop(self):
+#        code = """
+#for i in []:
+#    print i
+#"""
+#        ast = self.compileast(code)
+#        self.assertEqual(_ast.Pass, ast.body[0].__class__)
+#
+#    def test_skip_unreachable_while_loop(self):
+#        code = """
+#while 0:
+#    print 'foo'
+#"""
+#
+#        ast = self.compileast(code)
+#        self.assertEqual(_ast.Pass, ast.body[0].__class__)
+
+#    def test_fold_constant_list_in_for_loop(self):
+#        code = """
+#for i in [1, 2, 3]:
+#    print i
+#"""
+#        ast = self.compileast(code)
+#        self.assertEqual(_ast.Const, ast.body[0].iter.__class__)
+#        self.assertEqual((1, 2, 3), ast.body[0].iter.value)
 
     def test_named_constants(self):
         tests = [None]
@@ -274,6 +274,32 @@
         self.assertEqual(_ast.Return, ast.body[0].body[0].body[1].__class__)
         self.assertEqual('y', ast.body[0].body[0].body[1].value.id)
 
+        code = """
+def bar(x):
+    try:
+        print 'test'
+    except:
+        print 'except'
+    return 1
+"""
+
+        ast = self.compileast(code)
+        self.assertEqual(_ast.Return, ast.body[0].body[0].body[1].__class__)
+        self.assertEqual(1, ast.body[0].body[0].body[1].value.n)
+
+#    def test_jump_to_implicit_returns_are_simplified(self):
+#        code = """
+#def foo(x):
+#    if x:
+#        print x
+#    else:
+#        print "n/a"
+#"""
+#
+#        ast = self.compileast(code)
+#        self.assertEqual(_ast.Return, ast.body[0].body[0].body[1].__class__)
+#        self.assertEqual(None, ast.body[0].body[0].body[1].value)
+
     def test_assignment_to_true_works(self):
         # ---------------------------------------------------------------------
         #   Please note that this test is for <2.6 to ensure that

Modified: python/branches/tlee-ast-optimize/Python/optimize.c
==============================================================================
--- python/branches/tlee-ast-optimize/Python/optimize.c	(original)
+++ python/branches/tlee-ast-optimize/Python/optimize.c	Wed Jun 25 15:01:28 2008
@@ -143,34 +143,6 @@
 }
 
 /**
- */
-static asdl_seq*
-_asdl_seq_append(asdl_seq* seq1, int n1, asdl_seq* seq2, int n2,
-                    PyArena* arena)
-{
-    asdl_seq* new;
-    int newlen, i;
-    int len1, len2;
-
-    /* XXX: check this calculation */
-    len1 = asdl_seq_LEN(seq1) - n1;
-    len2 = asdl_seq_LEN(seq2) - n2;
-    newlen = len1 + len2;
-
-    new = asdl_seq_new(newlen, arena);
-    if (new == NULL)
-        return NULL;
-
-    for (i = 0; i < len1; i++)
-        asdl_seq_SET(new, i, asdl_seq_GET(seq1, n1 + i));
-
-    for (i = 0; i < len2; i++)
-        asdl_seq_SET(new, len1 + i, asdl_seq_GET(seq2, n2 + i));
-
-    return new;
-}
-
-/**
  * Replace an AST node at position `n' with the node(s) in `replacement'.
  */
 static asdl_seq*
@@ -224,6 +196,12 @@
 
 #define LAST_IN_SEQ(seq) (asdl_seq_LEN((seq)) - 1)
 
+/* XXX: code elimination must be disabled until we work out what to do with
+ *      "continue" statements in a "finally" clause. If we eliminate a
+ *      branch containing a "try..finally" with a finally clause containing
+ *      a "continue" statement, we start breaking tests.
+ */
+#if 0
 /**
  * Eliminate code that we can determine will never be executed.
  */
@@ -303,7 +281,40 @@
 
     return 1;
 }
+#endif
+
+/**
+ * Adds all ASDL nodes from seq1 with offset n1 to a new list, then
+ * appends the nodes in seq2 from offset n2. The resulting list is
+ * returned.
+ */
+static asdl_seq*
+_asdl_seq_append(asdl_seq* seq1, int n1, asdl_seq* seq2, int n2,
+                    PyArena* arena)
+{
+    asdl_seq* new;
+    int i;
+    int len1 = asdl_seq_LEN(seq1) - n1;
+    int len2 = asdl_seq_LEN(seq2) - n2;
+    int newlen = len1 + len2;
+
+    new = asdl_seq_new(newlen, arena);
+    if (new == NULL)
+        return NULL;
+
+    for (i = 0; i < len1; i++)
+        asdl_seq_SET(new, i, asdl_seq_GET(seq1, n1 + i));
+
+    for (i = 0; i < len2; i++)
+        asdl_seq_SET(new, len1 + i, asdl_seq_GET(seq2, n2 + i));
+
+    return new;
+}
 
+/**
+ * Appends a Return node to the end of `seq' using the given
+ * value.
+ */
 static asdl_seq*
 _asdl_seq_append_return(asdl_seq* seq, expr_ty value, PyArena* arena)
 {
@@ -320,37 +331,102 @@
     return _asdl_seq_append(seq, 0, retseq, 0, arena);
 }
 
+static int
+_inject_compound_stmt_return(stmt_ty stmt, stmt_ty next, PyArena* arena)
+{
+    /* if the else body is not present, there will be no jump anyway */
+    if (stmt->kind == If_kind && stmt->v.If.orelse != NULL) {
+        stmt_ty inner = asdl_seq_GET(stmt->v.If.body,
+                                    LAST_IN_SEQ(stmt->v.If.body));
+        
+        if (inner->kind != Return_kind) {
+            stmt->v.If.body =
+                _asdl_seq_append_return(stmt->v.If.body,
+                                        next->v.Return.value, arena);
+
+            if (stmt->v.If.body == NULL)
+                return 0;
+        }
+    }
+    /* TODO: we probably want to append a return to all but
+     *       the last handler too
+     */
+    else if (stmt->kind == TryExcept_kind) {
+        stmt_ty inner = asdl_seq_GET(stmt->v.TryExcept.body,
+                            LAST_IN_SEQ(stmt->v.TryExcept.body));
+        if (inner->kind != Return_kind && inner->kind != Raise_kind) {
+            /* XXX: if we inject a return into the "try" body of a
+             *      "try..except..else", we will miss the "else"!
+             *      We need to take a different path if the "else" is
+             *      present.
+             */
+            if (stmt->v.TryExcept.orelse == NULL) {
+                stmt->v.TryExcept.body =
+                    _asdl_seq_append_return(stmt->v.TryExcept.body,
+                                            next->v.Return.value, arena);
+                if (stmt->v.TryExcept.body == NULL)
+                    return 0;
+            }
+            else {
+                stmt->v.TryExcept.orelse =
+                    _asdl_seq_append_return(stmt->v.TryExcept.orelse,
+                                            next->v.Return.value, arena);
+                if (stmt->v.TryExcept.orelse == NULL)
+                    return 0;
+            }
+        }
+    }
+    else if (stmt->kind == TryFinally_kind) {
+        stmt_ty inner = asdl_seq_GET(stmt->v.TryFinally.body,
+                            LAST_IN_SEQ(stmt->v.TryFinally.body));
+        if (inner->kind != Return_kind && inner->kind != Raise_kind) {
+            stmt->v.TryFinally.body =
+                _asdl_seq_append_return(stmt->v.TryFinally.body,
+                                        next->v.Return.value, arena);
+            if (stmt->v.TryFinally.body == NULL)
+                return 0;
+        }
+    }
+
+    return 1;
+}
+
 /**
  * Simplify any branches that converge on a "return" statement such that
  * they immediately return rather than jump.
  */
 static int
-_simplify_jumps_to_return(asdl_seq* seq, PySTEntryObject* ste,
-                            PyArena* arena)
+_simplify_jumps(asdl_seq* seq, PySTEntryObject* ste, PyArena* arena)
 {
     int n, len;
 
     len = asdl_seq_LEN(seq);
 
     for (n = 0; n < len - 1; n++) {
-        stmt_ty stmt = asdl_seq_GET(seq, n);
-        stmt_ty next = asdl_seq_GET(seq, n+1);
+        stmt_ty stmt;
+        stmt_ty next;
         
-        if (next->kind == Return_kind) {
-            /* if the else body is not present, there will be no jump */
-            if (stmt->kind == If_kind && stmt->v.If.orelse != NULL) {
-                stmt_ty inner = asdl_seq_GET(stmt->v.If.body,
-                                            asdl_seq_LEN(stmt->v.If.body)-1);
-                
-                if (inner->kind != Return_kind) {
-                    stmt->v.If.body =
-                        _asdl_seq_append_return(stmt->v.If.body,
-                                                next->v.Return.value, arena);
+        stmt = asdl_seq_GET(seq, n);
 
-                    if (stmt->v.If.body == NULL)
-                        return 0;
-                }
-            }
+/* XXX: this will only work at the top level of a function ... let's find
+ *      somewhere else for this optimization to live! */
+#if 0
+        /* on the last iteration, the target is an implicit `return None' */
+        if (n == len-1) {
+            if (stmt->kind == Return_kind)
+                continue;
+
+            next = Return(NULL, stmt->lineno, stmt->col_offset, arena);
+            if (next == NULL)
+                return 0;
+        }
+        else
+#endif
+        next = asdl_seq_GET(seq, n+1);
+        
+        if (next->kind == Return_kind) {
+            if (!_inject_compound_stmt_return(stmt, next, arena))
+                return 0;
         }
     }
 
@@ -368,10 +444,12 @@
     for (n = 0; n < asdl_seq_LEN(seq); n++) {
         if (!optimize_stmt((stmt_ty*)&asdl_seq_GET(seq, n), ste, arena))
             return 0;
+#if 0
         if (!_eliminate_unreachable_code(seq_ptr, n, ste, arena))
             return 0;
+#endif
         if (ste->ste_type == FunctionBlock)
-            if (!_simplify_jumps_to_return(*seq_ptr, ste, arena))
+            if (!_simplify_jumps(*seq_ptr, ste, arena))
                 return 0;
     }
     return 1;
@@ -564,7 +642,7 @@
                 }
             case FloorDiv:
                 {
-                    /* avoid divide-by-zero errors */
+                    /* raise divide-by-zero errors at runtime */
                     if (PyObject_IsTrue(right))
                         res = PyNumber_FloorDivide(left, right);
                     break;
@@ -1114,24 +1192,6 @@
     stmt_ty stmt = *stmt_ptr;
     if (!optimize_expr(&stmt->v.For.iter, ste, arena))
         return 0;
-    /* if the object we're iterating over is a list of constants,
-     * build the list at compile time. Note that this will actually
-     * transform the list into a tuple. This is safe because only
-     * the `for' loop can actually reference it.
-     */
-    if (stmt->v.For.iter->kind == List_kind) {
-        expr_ty list = stmt->v.For.iter;
-        if (_is_sequence_of_constants(list->v.List.elts)) {
-            PyObject* iter = _build_tuple_of_constants(list->v.List.elts,
-                                                        arena);
-            if (iter == NULL)
-                return 0;
-            stmt->v.For.iter = Const(iter, stmt->lineno, stmt->col_offset,
-                                        arena);
-            if (stmt->v.For.iter == NULL)
-                return 0;
-        }
-    }
     return 1;
 }
 
@@ -1416,6 +1476,6 @@
         return 0;
     result = optimize_mod(mod_ptr, ste, arena);
     Py_DECREF(ste);
-    return 1;
+    return result;
 }
 


More information about the Python-checkins mailing list