[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