Index: Python/optimize.c =================================================================== --- Python/optimize.c (revision 67048) +++ Python/optimize.c (working copy) @@ -6,9 +6,18 @@ #include "node.h" #include "ast.h" +typedef struct _call_site { + stmt_ty stmt; + struct _call_site* cs_next; + identifier name; +} call_site; + typedef struct _optimizer_block { struct _optimizer_block* b_next; /* next block on the stack */ PySTEntryObject* b_ste; /* symtable entry */ + PyObject* local_types; + call_site* append_call_sites; + PyObject* local_type_dependencies; /* we don't want to optimize away "try" blocks containing a * "continue" statement in its finally clause. This is illegal syntax, @@ -25,7 +34,13 @@ optimizer_block* opt_current; /* the current block */ } optimizer; -static int optimize_expr(optimizer* opt, expr_ty* expr_ptr); +enum local_types { + List_type=0, + Append_type=1, + Mixed_type=-1 +}; + +static int optimize_expr(optimizer* opt, expr_ty* expr_ptr, stmt_ty* stmt); static int optimize_stmt(optimizer* opt, stmt_ty* stmt_ptr); static int optimize_comprehension(optimizer* opt, comprehension_ty* comp_ptr); static int optimize_excepthandler(optimizer* opt, excepthandler_ty* exc_ptr); @@ -46,7 +61,23 @@ block->b_finally = 0; block->b_eliminate = 1; block->b_ste = ste; + block->local_types = PyDict_New(); + if (!block->local_types) { + free(block); + return 0; + } + + block->local_type_dependencies = PyDict_New(); + + if (!block->local_type_dependencies) { + Py_DECREF(block->local_types); + free(block); + return 0; + } + + block->append_call_sites = NULL; + Py_INCREF(ste); opt->opt_current = block; @@ -65,6 +96,15 @@ next = opt->opt_current->b_next; Py_DECREF(opt->opt_current->b_ste); + Py_DECREF(opt->opt_current->local_types); + Py_DECREF(opt->opt_current->local_type_dependencies); + + call_site *cs = opt->opt_current->append_call_sites; + while (cs) { + call_site* next = cs->cs_next; + free(cs); + cs = next; + } free(opt->opt_current); opt->opt_current = next; @@ -187,7 +227,7 @@ int n; asdl_seq* seq = *seq_ptr; for (n = 0; n < asdl_seq_LEN(seq); n++) - if (!optimize_expr(opt, (expr_ty*)&asdl_seq_GET(seq, n))) + if (!optimize_expr(opt, (expr_ty*)&asdl_seq_GET(seq, n), NULL)) return 0; return 1; } @@ -482,6 +522,108 @@ return 1; } +static int _add_call_site(optimizer* opt, stmt_ty stmt, PyObject* name) { + call_site* new_cs; + new_cs = malloc(sizeof (call_site)); + if (new_cs == NULL) { + PyErr_Format(PyExc_MemoryError, "failed to allocate memory in optimzer at %d", stmt->lineno); + return 0; + } + new_cs->cs_next = opt->opt_current->append_call_sites; + new_cs->stmt = stmt; + new_cs->name = name; + opt->opt_current->append_call_sites = new_cs; + return 1; +} + +static int _local_type_depends(optimizer* opt, PyObject* child, PyObject* parent) { + PyObject* deps = opt->opt_current->local_type_dependencies; + if (PyDict_SetItem(deps, child, parent)) { + return 0; + } + return 1; +} + +static int _update_local_type_info(optimizer* opt, expr_ty target, expr_ty value) { + + int i; + if (target->kind == Tuple_kind) { + int val_is_tuple = value && value->kind == Tuple_kind; + + asdl_seq* targets = target->v.Tuple.elts; + for (i = 0; i < asdl_seq_LEN(targets); i++) { + expr_ty targ = (expr_ty) asdl_seq_GET(targets, i); + if (val_is_tuple) { + if (!_update_local_type_info(opt, targ, asdl_seq_GET(value->v.Tuple.elts, i))) + return 0; + } else { + if (!_update_local_type_info(opt, targ, NULL)) + return 0; + } + } + return 1; + + } else if (target->kind != Name_kind) { + return 1; + } + + int scope = PyST_GetScope(opt->opt_current->b_ste, target->v.Name.id); + if (scope != 1) return 1; + + PyObject* locals = opt->opt_current->local_types; + int newcode = Mixed_type; + PyObject* newcode_o; + if (value == NULL) + goto do_assign; + if (PyDict_Contains(locals, target->v.Name.id)) { + PyObject* result = PyDict_GetItem(locals, target->v.Name.id); + int code = PyInt_AsLong (result); + if (code == Mixed_type) + return 1; + if (code == List_type) { + /* we have seen this local before, and it held a list */ + if (value->kind == ListComp_kind || value->kind == List_kind) { + return 1; + } else { + /*TODO: we could actually check to see if the value is another local + which holds a list -- we would need to add a dependency for + that. */ + newcode = Mixed_type; + } + } else if (code == Append_type) { + /* we only optimize locals that hold one append -- otherwise, + we have to worry about which list we're calling append on */ + newcode = Mixed_type; + } + } else { + if (value->kind == ListComp_kind || value->kind == List_kind) + newcode = List_type; + else if (value->kind == Attribute_kind) { + if (strcmp(PyString_AsString(value->v.Attribute.attr), "append")) { + newcode = Mixed_type; + goto do_assign; + } + value = value->v.Attribute.value; + if (value->kind != Name_kind) { + newcode = Mixed_type; + goto do_assign; + } + if (!_local_type_depends(opt, target->v.Name.id, value->v.Name.id)) + return 0; + newcode = Append_type; + } + } + do_assign: + newcode_o = PyInt_FromLong(newcode); + if (newcode_o == NULL) + return 0; + if (PyDict_SetItem(locals, target->v.Name.id, newcode_o)) + return 0; + Py_DECREF(newcode_o); + return 1; +} + + /** * Optimize a sequence of statements. */ @@ -577,7 +719,7 @@ } case Expression_kind: { - return optimize_expr(opt, &mod->v.Expression.body); + return optimize_expr(opt, &mod->v.Expression.body, NULL); } default: PyErr_Format(PyExc_ValueError, "unknown mod_ty kind: %d", @@ -604,9 +746,9 @@ PyObject* right; expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.BinOp.left)) + if (!optimize_expr(opt, &expr->v.BinOp.left, NULL)) return 0; - if (!optimize_expr(opt, &expr->v.BinOp.right)) + if (!optimize_expr(opt, &expr->v.BinOp.right, NULL)) return 0; /* @@ -738,7 +880,7 @@ { PyObject* operand; expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.UnaryOp.operand)) + if (!optimize_expr(opt, &expr->v.UnaryOp.operand, NULL)) return 0; operand = _expr_constant_value(expr->v.UnaryOp.operand); if (operand != NULL) { @@ -803,7 +945,7 @@ optimize_lambda(optimizer* opt, expr_ty* expr_ptr) { expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.Lambda.body)) + if (!optimize_expr(opt, &expr->v.Lambda.body, NULL)) return 0; return 1; } @@ -812,11 +954,11 @@ optimize_if_exp(optimizer* opt, expr_ty* expr_ptr) { expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.IfExp.test)) + if (!optimize_expr(opt, &expr->v.IfExp.test, NULL)) return 0; - if (!optimize_expr(opt, &expr->v.IfExp.body)) + if (!optimize_expr(opt, &expr->v.IfExp.body, NULL)) return 0; - if (!optimize_expr(opt, &expr->v.IfExp.orelse)) + if (!optimize_expr(opt, &expr->v.IfExp.orelse, NULL)) return 0; return 1; } @@ -836,9 +978,9 @@ optimize_comprehension(optimizer* opt, comprehension_ty* comp_ptr) { comprehension_ty comp = *comp_ptr; - if (!optimize_expr(opt, &comp->target)) + if (!optimize_expr(opt, &comp->target, NULL)) return 0; - if (!optimize_expr(opt, &comp->iter)) + if (!optimize_expr(opt, &comp->iter, NULL)) return 0; if (!optimize_expr_seq(opt, &comp->ifs)) return 0; @@ -849,7 +991,7 @@ optimize_list_comp(optimizer* opt, expr_ty* expr_ptr) { expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.ListComp.elt)) + if (!optimize_expr(opt, &expr->v.ListComp.elt, NULL)) return 0; if (!optimize_comprehension_seq(opt, &expr->v.ListComp.generators)) return 0; @@ -860,7 +1002,7 @@ optimize_generator_exp(optimizer* opt, expr_ty* expr_ptr) { expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.GeneratorExp.elt)) + if (!optimize_expr(opt, &expr->v.GeneratorExp.elt, NULL)) return 0; if (!optimize_comprehension_seq(opt, &expr->v.GeneratorExp.generators)) return 0; @@ -873,7 +1015,7 @@ expr_ty expr = *expr_ptr; if (expr->v.Yield.value != NULL) { expr_ty value; - if (!optimize_expr(opt, &expr->v.Yield.value)) + if (!optimize_expr(opt, &expr->v.Yield.value, NULL)) return 0; value = expr->v.Yield.value; if (value->kind == Const_kind && value->v.Const.value == Py_None) @@ -886,7 +1028,7 @@ optimize_compare(optimizer* opt, expr_ty* expr_ptr) { expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.Compare.left)) + if (!optimize_expr(opt, &expr->v.Compare.left, NULL)) return 0; if (!optimize_expr_seq(opt, &expr->v.Compare.comparators)) return 0; @@ -897,7 +1039,7 @@ optimize_keyword(optimizer* opt, keyword_ty* keyword_ptr) { keyword_ty keyword = *keyword_ptr; - if (!optimize_expr(opt, &keyword->value)) + if (!optimize_expr(opt, &keyword->value, NULL)) return 0; return 1; } @@ -914,20 +1056,43 @@ } static int -optimize_call(optimizer* opt, expr_ty* expr_ptr) +optimize_call(optimizer* opt, expr_ty* expr_ptr, stmt_ty* stmt) { - expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.Call.func)) + expr_ty expr=*expr_ptr; + if (stmt == NULL) goto not_append; + + if (asdl_seq_LEN(expr->v.Call.args) != 1) goto not_append; + if (asdl_seq_LEN(expr->v.Call.keywords) != 0) goto not_append; + if (expr->v.Call.starargs) goto not_append; + if (expr->v.Call.kwargs) goto not_append; + expr_ty func = expr->v.Call.func; + + + if (func->kind == Attribute_kind) { + if (strcmp(PyString_AsString(func->v.Attribute.attr), "append")) + goto not_append; + + expr_ty base = func->v.Attribute.value; + if (base->kind != Name_kind) goto not_append; + + _add_call_site(opt, *stmt, NULL); + } else if (func->kind == Name_kind) { + _add_call_site(opt, *stmt, func->v.Name.id); + } + + not_append: + + if (!optimize_expr(opt, &expr->v.Call.func, NULL)) return 0; if (!optimize_expr_seq(opt, &expr->v.Call.args)) return 0; if (!optimize_keyword_seq(opt, &expr->v.Call.keywords)) return 0; if (expr->v.Call.starargs != NULL) - if (!optimize_expr(opt, &expr->v.Call.starargs)) + if (!optimize_expr(opt, &expr->v.Call.starargs, NULL)) return 0; if (expr->v.Call.kwargs != NULL) - if (!optimize_expr(opt, &expr->v.Call.kwargs)) + if (!optimize_expr(opt, &expr->v.Call.kwargs, NULL)) return 0; return 1; } @@ -936,7 +1101,7 @@ optimize_repr(optimizer* opt, expr_ty* expr_ptr) { expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.Repr.value)) + if (!optimize_expr(opt, &expr->v.Repr.value, NULL)) return 0; return 1; } @@ -945,7 +1110,7 @@ optimize_attribute(optimizer* opt, expr_ty* expr_ptr) { expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.Attribute.value)) + if (!optimize_expr(opt, &expr->v.Attribute.value, NULL)) return 0; return 1; } @@ -958,13 +1123,13 @@ case Slice_kind: { if (slice->v.Slice.lower != NULL) - if (!optimize_expr(opt, &slice->v.Slice.lower)) + if (!optimize_expr(opt, &slice->v.Slice.lower, NULL)) return 0; if (slice->v.Slice.upper != NULL) - if (!optimize_expr(opt, &slice->v.Slice.upper)) + if (!optimize_expr(opt, &slice->v.Slice.upper, NULL)) return 0; if (slice->v.Slice.step != NULL) - if (!optimize_expr(opt, &slice->v.Slice.step)) + if (!optimize_expr(opt, &slice->v.Slice.step, NULL)) return 0; break; } @@ -976,7 +1141,7 @@ } case Index_kind: { - if (!optimize_expr(opt, &slice->v.Index.value)) + if (!optimize_expr(opt, &slice->v.Index.value, NULL)) return 0; break; } @@ -996,7 +1161,7 @@ optimize_subscript(optimizer* opt, expr_ty* expr_ptr) { expr_ty expr = *expr_ptr; - if (!optimize_expr(opt, &expr->v.Subscript.value)) + if (!optimize_expr(opt, &expr->v.Subscript.value, NULL)) return 0; if (!optimize_slice(opt, &expr->v.Subscript.slice)) return 0; @@ -1062,7 +1227,7 @@ } static int -optimize_expr(optimizer* opt, expr_ty* expr_ptr) +optimize_expr(optimizer* opt, expr_ty* expr_ptr, stmt_ty* stmt) { expr_ty expr = *expr_ptr; switch (expr->kind) { @@ -1108,7 +1273,7 @@ } case Call_kind: { - return optimize_call(opt, expr_ptr); + return optimize_call(opt, expr_ptr, stmt); } case Repr_kind: { @@ -1162,6 +1327,7 @@ optimize_function_def(optimizer* opt, stmt_ty* stmt_ptr) { stmt_ty stmt = *stmt_ptr; + call_site* cs; /* XXX: this breaks a bunch of tests. For now we use a second pass. */ #if 0 @@ -1181,6 +1347,53 @@ return 0; if (!optimize_stmt_seq(opt, &stmt->v.FunctionDef.body)) return 0; + + /* replace calls to list.append with LIST_APPEND opcodes -- but + only when it can be statically determined that list is always + a list. */ + for (cs = opt->opt_current->append_call_sites; cs; cs = cs->cs_next) { + identifier name; + stmt_ty stmt = cs->stmt; + expr_ty expr = stmt->v.Expr.value; + expr_ty func = expr->v.Call.func; + + if (cs->name) { + + if (!PyDict_Contains(opt->opt_current->local_types, cs->name)) continue; + int name_code = PyInt_AsLong(PyDict_GetItem(opt->opt_current->local_types, cs->name)); + if (name_code != Append_type) + continue; + + if (!PyDict_Contains(opt->opt_current->local_type_dependencies, + cs->name)) + continue; + + name = PyDict_GetItem(opt->opt_current->local_type_dependencies, + cs->name); + + if (!PyDict_Contains(opt->opt_current->local_types, name)) + continue; + + int local_code = PyInt_AsLong(PyDict_GetItem(opt->opt_current->local_types, name)); + + if (local_code != List_type) + continue; + } else { + expr_ty base = func->v.Attribute.value; + name = base->v.Name.id; + + if (!PyDict_Contains(opt->opt_current->local_types, name)) continue; + int code = PyInt_AsLong(PyDict_GetItem(opt->opt_current->local_types, name)); + if (code != List_type) continue; + } + expr_ty arg = (expr_ty) asdl_seq_GET(expr->v.Call.args, 0); + + stmt->kind = Append_kind; + stmt->v.Append.target = name; + stmt->v.Append.value = arg; + + } + /* XXX: shallow second pass for implicit returns */ if (!_contains_return(stmt->v.FunctionDef.body)) if (!_simplify_jumps(opt, stmt->v.FunctionDef.body, 1)) @@ -1207,7 +1420,7 @@ stmt_ty stmt = *stmt_ptr; if (stmt->v.Return.value != NULL) { expr_ty value; - if (!optimize_expr(opt, &stmt->v.Return.value)) + if (!optimize_expr(opt, &stmt->v.Return.value, NULL)) return 0; value = stmt->v.Return.value; if (value->kind == Const_kind && value->v.Const.value == Py_None) @@ -1231,8 +1444,21 @@ stmt_ty stmt = *stmt_ptr; if (!optimize_expr_seq(opt, &stmt->v.Assign.targets)) return 0; - if (!optimize_expr(opt, &stmt->v.Assign.value)) + if (!optimize_expr(opt, &stmt->v.Assign.value, NULL)) return 0; + + /* for tuples, we really want to unpack the args too (and if impossible, we want to recursively set to 0) S*/ + + asdl_seq* targets = stmt->v.Assign.targets; + expr_ty value = stmt->v.Assign.value; + + int i; + for (i = 0; i < asdl_seq_LEN(targets); i++) { + expr_ty target = (expr_ty) asdl_seq_GET(targets, i); + if (!_update_local_type_info(opt, target, value)) + return 0; + } + return 1; } @@ -1240,9 +1466,9 @@ optimize_aug_assign(optimizer* opt, stmt_ty* stmt_ptr) { stmt_ty stmt = *stmt_ptr; - if (!optimize_expr(opt, &stmt->v.AugAssign.target)) + if (!optimize_expr(opt, &stmt->v.AugAssign.target, NULL)) return 0; - if (!optimize_expr(opt, &stmt->v.AugAssign.value)) + if (!optimize_expr(opt, &stmt->v.AugAssign.value, NULL)) return 0; return 1; } @@ -1253,7 +1479,7 @@ stmt_ty stmt = *stmt_ptr; if (stmt->v.Print.dest != NULL) - if (!optimize_expr(opt, &stmt->v.Print.dest)) + if (!optimize_expr(opt, &stmt->v.Print.dest, NULL)) return 0; if (!optimize_expr_seq(opt, &stmt->v.Print.values)) return 0; @@ -1264,7 +1490,7 @@ _optimize_for_iter(optimizer* opt, stmt_ty* stmt_ptr) { stmt_ty stmt = *stmt_ptr; - if (!optimize_expr(opt, &stmt->v.For.iter)) + if (!optimize_expr(opt, &stmt->v.For.iter, NULL)) return 0; return 1; } @@ -1273,8 +1499,14 @@ optimize_for(optimizer* opt, stmt_ty* stmt_ptr) { stmt_ty stmt = *stmt_ptr; - if (!optimize_expr(opt, &stmt->v.For.target)) + expr_ty target = stmt->v.For.target; + + if (!_update_local_type_info(opt, target, NULL)) { return 0; + } + + if (!optimize_expr(opt, &stmt->v.For.target, NULL)) + return 0; if (!_optimize_for_iter(opt, &stmt)) return 0; if (!optimize_stmt_seq(opt, &stmt->v.For.body)) @@ -1288,7 +1520,7 @@ optimize_while(optimizer* opt, stmt_ty* stmt_ptr) { stmt_ty stmt = *stmt_ptr; - if (!optimize_expr(opt, &stmt->v.While.test)) + if (!optimize_expr(opt, &stmt->v.While.test, NULL)) return 0; if (!optimize_stmt_seq(opt, &stmt->v.While.body)) return 0; @@ -1302,7 +1534,7 @@ { stmt_ty stmt = *stmt_ptr; - if (!optimize_expr(opt, &stmt->v.If.test)) + if (!optimize_expr(opt, &stmt->v.If.test, NULL)) return 0; if (!optimize_stmt_seq(opt, &stmt->v.If.body)) return 0; @@ -1342,10 +1574,10 @@ optimize_with(optimizer* opt, stmt_ty* stmt_ptr) { stmt_ty stmt = *stmt_ptr; - if (!optimize_expr(opt, &stmt->v.With.context_expr)) + if (!optimize_expr(opt, &stmt->v.With.context_expr, NULL)) return 0; if (stmt->v.With.optional_vars != NULL) - if (!optimize_expr(opt, &stmt->v.With.optional_vars)) + if (!optimize_expr(opt, &stmt->v.With.optional_vars, NULL)) return 0; if (!optimize_stmt_seq(opt, &stmt->v.With.body)) return 0; @@ -1357,13 +1589,13 @@ { stmt_ty stmt = *stmt_ptr; if (stmt->v.Raise.type != NULL) - if (!optimize_expr(opt, &stmt->v.Raise.type)) + if (!optimize_expr(opt, &stmt->v.Raise.type, NULL)) return 0; if (stmt->v.Raise.inst != NULL) - if (!optimize_expr(opt, &stmt->v.Raise.inst)) + if (!optimize_expr(opt, &stmt->v.Raise.inst, NULL)) return 0; if (stmt->v.Raise.tback != NULL) - if (!optimize_expr(opt, &stmt->v.Raise.tback)) + if (!optimize_expr(opt, &stmt->v.Raise.tback, NULL)) return 0; return 1; } @@ -1373,10 +1605,10 @@ { excepthandler_ty exc = *exc_ptr; if (exc->v.ExceptHandler.type != NULL) - if (!optimize_expr(opt, &exc->v.ExceptHandler.type)) + if (!optimize_expr(opt, &exc->v.ExceptHandler.type, NULL)) return 0; if (exc->v.ExceptHandler.name != NULL) - if (!optimize_expr(opt, &exc->v.ExceptHandler.name)) + if (!optimize_expr(opt, &exc->v.ExceptHandler.name, NULL)) return 0; if (!optimize_stmt_seq(opt, &exc->v.ExceptHandler.body)) return 0; @@ -1418,10 +1650,10 @@ optimize_assert(optimizer* opt, stmt_ty* stmt_ptr) { stmt_ty stmt = *stmt_ptr; - if (!optimize_expr(opt, &stmt->v.Assert.test)) + if (!optimize_expr(opt, &stmt->v.Assert.test, NULL)) return 0; if (stmt->v.Assert.msg != NULL) - if (!optimize_expr(opt, &stmt->v.Assert.msg)) + if (!optimize_expr(opt, &stmt->v.Assert.msg, NULL)) return 0; return 1; } @@ -1430,13 +1662,13 @@ optimize_exec(optimizer* opt, stmt_ty* stmt_ptr) { stmt_ty stmt = *stmt_ptr; - if (!optimize_expr(opt, &stmt->v.Exec.body)) + if (!optimize_expr(opt, &stmt->v.Exec.body, NULL)) return 0; if (stmt->v.Exec.globals != NULL) - if (!optimize_expr(opt, &stmt->v.Exec.globals)) + if (!optimize_expr(opt, &stmt->v.Exec.globals, NULL)) return 0; if (stmt->v.Exec.locals != NULL) - if (!optimize_expr(opt, &stmt->v.Exec.locals)) + if (!optimize_expr(opt, &stmt->v.Exec.locals, NULL)) return 0; return 1; } @@ -1541,7 +1773,7 @@ } case Expr_kind: { - return optimize_expr(opt, &stmt->v.Expr.value); + return optimize_expr(opt, &stmt->v.Expr.value, stmt_ptr); } case Import_kind: case ImportFrom_kind: Index: Python/compile.c =================================================================== --- Python/compile.c (revision 67048) +++ Python/compile.c (working copy) @@ -2176,6 +2176,12 @@ case Delete_kind: VISIT_SEQ(c, expr, s->v.Delete.targets) break; + case Append_kind: + if (!compiler_nameop(c, s->v.Append.target, Load)) + return 0; + VISIT(c, expr, s->v.Append.value); + ADDOP(c, LIST_APPEND); + break; case Assign_kind: n = asdl_seq_LEN(s->v.Assign.targets); VISIT(c, expr, s->v.Assign.value); Index: Python/Python-ast.c =================================================================== --- Python/Python-ast.c (revision 67048) +++ Python/Python-ast.c (working copy) @@ -64,6 +64,11 @@ "targets", "value", }; +static PyTypeObject *Append_type; +static char *Append_fields[]={ + "target", + "value", +}; static PyTypeObject *AugAssign_type; static char *AugAssign_fields[]={ "target", @@ -651,6 +656,8 @@ if (!Delete_type) return 0; Assign_type = make_type("Assign", stmt_type, Assign_fields, 2); if (!Assign_type) return 0; + Append_type = make_type("Append", stmt_type, Append_fields, 2); + if (!Append_type) return 0; AugAssign_type = make_type("AugAssign", stmt_type, AugAssign_fields, 3); if (!AugAssign_type) return 0; Print_type = make_type("Print", stmt_type, Print_fields, 3); @@ -1087,6 +1094,32 @@ } stmt_ty +Append(identifier target, expr_ty value, int lineno, int col_offset, PyArena + *arena) +{ + stmt_ty p; + if (!target) { + PyErr_SetString(PyExc_ValueError, + "field target is required for Append"); + return NULL; + } + if (!value) { + PyErr_SetString(PyExc_ValueError, + "field value is required for Append"); + return NULL; + } + p = (stmt_ty)PyArena_Malloc(arena, sizeof(*p)); + if (!p) + return NULL; + p->kind = Append_kind; + p->v.Append.target = target; + p->v.Append.value = value; + p->lineno = lineno; + p->col_offset = col_offset; + return p; +} + +stmt_ty AugAssign(expr_ty target, operator_ty op, expr_ty value, int lineno, int col_offset, PyArena *arena) { @@ -2191,6 +2224,20 @@ goto failed; Py_DECREF(value); break; + case Append_kind: + result = PyType_GenericNew(Append_type, NULL, NULL); + if (!result) goto failed; + value = ast2obj_identifier(o->v.Append.target); + if (!value) goto failed; + if (PyObject_SetAttrString(result, "target", value) == -1) + goto failed; + Py_DECREF(value); + value = ast2obj_expr(o->v.Append.value); + if (!value) goto failed; + if (PyObject_SetAttrString(result, "value", value) == -1) + goto failed; + Py_DECREF(value); + break; case AugAssign_kind: result = PyType_GenericNew(AugAssign_type, NULL, NULL); if (!result) goto failed; @@ -3640,6 +3687,38 @@ if (*out == NULL) goto failed; return 0; } + if (PyObject_IsInstance(obj, (PyObject*)Append_type)) { + identifier target; + expr_ty value; + + if (PyObject_HasAttrString(obj, "target")) { + int res; + tmp = PyObject_GetAttrString(obj, "target"); + if (tmp == NULL) goto failed; + res = obj2ast_identifier(tmp, &target, arena); + if (res != 0) goto failed; + Py_XDECREF(tmp); + tmp = NULL; + } else { + PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from Append"); + return 1; + } + if (PyObject_HasAttrString(obj, "value")) { + int res; + tmp = PyObject_GetAttrString(obj, "value"); + if (tmp == NULL) goto failed; + res = obj2ast_expr(tmp, &value, arena); + if (res != 0) goto failed; + Py_XDECREF(tmp); + tmp = NULL; + } else { + PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Append"); + return 1; + } + *out = Append(target, value, lineno, col_offset, arena); + if (*out == NULL) goto failed; + return 0; + } if (PyObject_IsInstance(obj, (PyObject*)AugAssign_type)) { expr_ty target; operator_ty op; @@ -6013,6 +6092,8 @@ return; if (PyDict_SetItemString(d, "Assign", (PyObject*)Assign_type) < 0) return; + if (PyDict_SetItemString(d, "Append", (PyObject*)Append_type) < 0) + return; if (PyDict_SetItemString(d, "AugAssign", (PyObject*)AugAssign_type) < 0) return; if (PyDict_SetItemString(d, "Print", (PyObject*)Print_type) < 0) return; Index: Include/Python-ast.h =================================================================== --- Include/Python-ast.h (revision 67048) +++ Include/Python-ast.h (working copy) @@ -60,12 +60,12 @@ }; enum _stmt_kind {FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3, - Delete_kind=4, Assign_kind=5, AugAssign_kind=6, Print_kind=7, - For_kind=8, While_kind=9, If_kind=10, With_kind=11, - Raise_kind=12, TryExcept_kind=13, TryFinally_kind=14, - Assert_kind=15, Import_kind=16, ImportFrom_kind=17, - Exec_kind=18, Global_kind=19, Expr_kind=20, Pass_kind=21, - Break_kind=22, Continue_kind=23}; + Delete_kind=4, Assign_kind=5, Append_kind=6, + AugAssign_kind=7, Print_kind=8, For_kind=9, While_kind=10, + If_kind=11, With_kind=12, Raise_kind=13, TryExcept_kind=14, + TryFinally_kind=15, Assert_kind=16, Import_kind=17, + ImportFrom_kind=18, Exec_kind=19, Global_kind=20, + Expr_kind=21, Pass_kind=22, Break_kind=23, Continue_kind=24}; struct _stmt { enum _stmt_kind kind; union { @@ -97,6 +97,11 @@ } Assign; struct { + identifier target; + expr_ty value; + } Append; + + struct { expr_ty target; operator_ty op; expr_ty value; @@ -385,6 +390,9 @@ #define Assign(a0, a1, a2, a3, a4) _Py_Assign(a0, a1, a2, a3, a4) stmt_ty _Py_Assign(asdl_seq * targets, expr_ty value, int lineno, int col_offset, PyArena *arena); +#define Append(a0, a1, a2, a3, a4) _Py_Append(a0, a1, a2, a3, a4) +stmt_ty _Py_Append(identifier target, expr_ty value, int lineno, int + col_offset, PyArena *arena); #define AugAssign(a0, a1, a2, a3, a4, a5) _Py_AugAssign(a0, a1, a2, a3, a4, a5) stmt_ty _Py_AugAssign(expr_ty target, operator_ty op, expr_ty value, int lineno, int col_offset, PyArena *arena); Index: Parser/Python.asdl =================================================================== --- Parser/Python.asdl (revision 67048) +++ Parser/Python.asdl (working copy) @@ -16,6 +16,7 @@ | Delete(expr* targets) | Assign(expr* targets, expr value) + | Append(identifier target, expr value) | AugAssign(expr target, operator op, expr value) -- not sure if bool is allowed, can always use int Index: Lib/test/test_append.py =================================================================== --- Lib/test/test_append.py (revision 0) +++ Lib/test/test_append.py (revision 0) @@ -0,0 +1,196 @@ +#!/usr/bin/env python +""" +Test script for list optimization +Original by David Turner +""" + +from opcode import opmap +import unittest +from test import test_support +from test_peepholer import disassemble +import sys + +#this function will be optimized, because list is known to be a list +def ln1(x): + list = [] + list.append(x) + return list + +#similarly, this one +def ln2(x): + list = [] + a = list.append + a(x) + return list + +#and even this one +def ln3(x): + list = [] + a, (b, c) = list, (list.append, list) + b(x) + return list + +#this function will not be optimized, because list is not known to be a list +def la1(list): + list.append(0) + return list + +def _assemble(ops): + out = [] + for op in ops: + op_no = opmap[op[0]] + out.append(op_no) + if len(op) == 2: + out.append(op[1] & 0xff) + out.append((op[1] >> 8) & 0xff) + return "".join(map(chr, out)) + + +class OptimizeAppendTestCase(unittest.TestCase): + + def test_append(self): + la1_code = [ + ('LOAD_FAST', 0), #list + ('LOAD_ATTR', 0), #append + ('LOAD_CONST', 1), #0 + ('CALL_FUNCTION', 1), #append + ('POP_TOP',), #list + ('LOAD_FAST', 0), #list + ('RETURN_VALUE',), + ] + + ln1_code = [ + ('BUILD_LIST', 0), + ('STORE_FAST', 1), #list + ('LOAD_FAST', 1), #list + ('LOAD_FAST', 0), #x + ('LIST_APPEND',), + ('LOAD_FAST', 1), #list + ('RETURN_VALUE',), + ] + + ln2_code = [ + ('BUILD_LIST', 0), + ('STORE_FAST', 1), #list + ('LOAD_FAST', 1), #list + ('LOAD_ATTR', 0), #append + ('STORE_FAST', 2), #a + ('LOAD_FAST', 1), #list + ('LOAD_FAST', 0), #x + ('LIST_APPEND',), + ('LOAD_FAST', 1), #list + ('RETURN_VALUE',), + ] + + if not sys.flags.optimize: + print "Warning: can't test optimization unless running under -O" + return + + self.assertEqual(la1.func_code.co_code, _assemble(la1_code)) + self.assertEqual(ln1.func_code.co_code, _assemble(ln1_code)) + self.assertEqual(ln2.func_code.co_code, _assemble(ln2_code)) + self.assertTrue("LIST_APPEND" in disassemble(ln3)) + + +def test_main(): + test_support.run_unittest( + OptimizeAppendTestCase + ) + + +if __name__ == "__main__": + test_main() +#!/usr/bin/env python +""" +Test script for list optimization +Original by David Turner +""" + +from opcode import opmap +import unittest +from test import test_support +import sys + +#this function will be optimized, because list is known to be a list +def ln1(x): + list = [] + list.append(x) + return list + +#similarly, this one +def ln2(x): + list = [] + a = list.append + a(x) + return list + +#this function will not be optimized, because list is not known to be a list +def la1(list): + list.append(0) + return list + +def _assemble(ops): + out = [] + for op in ops: + op_no = opmap[op[0]] + out.append(op_no) + if len(op) == 2: + out.append(op[1] & 0xff) + out.append((op[1] >> 8) & 0xff) + return "".join(map(chr, out)) + + +class OptimizeAppendTestCase(unittest.TestCase): + + def test_append(self): + la1_code = [ + ('LOAD_FAST', 0), #list + ('LOAD_ATTR', 0), #append + ('LOAD_CONST', 1), #0 + ('CALL_FUNCTION', 1), #append + ('POP_TOP',), #list + ('LOAD_FAST', 0), #list + ('RETURN_VALUE',), + ] + + ln1_code = [ + ('BUILD_LIST', 0), + ('STORE_FAST', 1), #list + ('LOAD_FAST', 1), #list + ('LOAD_FAST', 0), #x + ('LIST_APPEND',), + ('LOAD_FAST', 1), #list + ('RETURN_VALUE',), + ] + + ln2_code = [ + ('BUILD_LIST', 0), + ('STORE_FAST', 1), #list + ('LOAD_FAST', 1), #list + ('LOAD_ATTR', 0), #append + ('STORE_FAST', 2), #a + ('LOAD_FAST', 1), #list + ('LOAD_FAST', 0), #x + ('LIST_APPEND',), + ('LOAD_FAST', 1), #list + ('RETURN_VALUE',), + ] + + if not sys.flags.optimize: + print "Warning: can't test optimization unless running under -O" + return + + self.assertEqual(la1.func_code.co_code, _assemble(la1_code)) + self.assertEqual(ln1.func_code.co_code, _assemble(ln1_code)) + self.assertEqual(ln2.func_code.co_code, _assemble(ln2_code)) + + + +def test_main(): + test_support.run_unittest( + OptimizeAppendTestCase + ) + + +if __name__ == "__main__": + test_main()