From f289ae6f01683689dfd07785c9617175b40aea91 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 18 Dec 2008 11:06:25 +0000 Subject: Merged revisions 67818 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r67818 | antoine.pitrou | 2008-12-17 01:38:28 +0100 (mer., 17 déc. 2008) | 3 lines Issue #2183: Simplify and optimize bytecode for list comprehensions. ........ --- Doc/library/dis.rst | 19 +++++++++++++++---- Include/opcode.h | 6 ++++-- Lib/opcode.py | 7 +++++-- Lib/test/test_dis.py | 36 ++++++++++++++++-------------------- Misc/NEWS | 3 +++ Python/ceval.c | 21 +++++++++++++++++---- Python/compile.c | 33 +++++++++------------------------ Python/import.c | 4 +++- 8 files changed, 72 insertions(+), 57 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 500b83f..ab06542 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -357,14 +357,25 @@ Miscellaneous opcodes. address to jump to (which should be a ``FOR_ITER`` instruction). -.. opcode:: SET_ADD () +.. opcode:: SET_ADD (i) - Calls ``set.add(TOS1, TOS)``. Used to implement set comprehensions. + Calls ``set.add(TOS1[-i], TOS)``. Used to implement set comprehensions. -.. opcode:: LIST_APPEND () +.. opcode:: LIST_APPEND (i) - Calls ``list.append(TOS1, TOS)``. Used to implement list comprehensions. + Calls ``list.append(TOS[-i], TOS)``. Used to implement list comprehensions. + + +.. opcode:: MAP_ADD (i) + + Calls ``dict.setitem(TOS1[-i], TOS, TOS1)``. Used to implement dict + comprehensions. + + +For all of the SET_ADD, LIST_APPEND and MAP_ADD instructions, while the +added value or key/value pair is popped off, the container object remains on +the stack so that it is available for further iterations of the loop. .. opcode:: LOAD_LOCALS () diff --git a/Include/opcode.h b/Include/opcode.h index 6da9877..063ab6c 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -21,8 +21,6 @@ extern "C" { #define UNARY_INVERT 15 -#define SET_ADD 17 -#define LIST_APPEND 18 #define BINARY_POWER 19 #define BINARY_MULTIPLY 20 @@ -133,6 +131,10 @@ extern "C" { /* Support for opargs more than 16 bits long */ #define EXTENDED_ARG 143 +#define LIST_APPEND 145 +#define SET_ADD 146 +#define MAP_ADD 147 + /* EXCEPT_HANDLER is a special, implicit block type which is created when entering an except handler. It is not an opcode but we define it here diff --git a/Lib/opcode.py b/Lib/opcode.py index 5fd9c21..9484d1e 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -57,8 +57,6 @@ def_op('UNARY_NOT', 12) def_op('UNARY_INVERT', 15) -def_op('SET_ADD', 17) -def_op('LIST_APPEND', 18) def_op('BINARY_POWER', 19) def_op('BINARY_MULTIPLY', 20) @@ -169,4 +167,9 @@ def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) def_op('EXTENDED_ARG', 143) EXTENDED_ARG = 143 +def_op('LIST_APPEND', 145) +def_op('SET_ADD', 146) +def_op('MAP_ADD', 147) + + del def_op, name_op, jrel_op, jabs_op diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index cf62ecf..d82dc5a 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -55,29 +55,25 @@ def bug1333982(x=[]): dis_bug1333982 = """\ %-4d 0 LOAD_CONST 1 (0) - 3 JUMP_IF_TRUE 41 (to 47) + 3 JUMP_IF_TRUE 33 (to 39) 6 POP_TOP 7 LOAD_GLOBAL 0 (AssertionError) 10 BUILD_LIST 0 - 13 DUP_TOP - 14 STORE_FAST 1 (_[1]) - 17 LOAD_FAST 0 (x) - 20 GET_ITER - >> 21 FOR_ITER 13 (to 37) - 24 STORE_FAST 2 (s) - 27 LOAD_FAST 1 (_[1]) - 30 LOAD_FAST 2 (s) - 33 LIST_APPEND - 34 JUMP_ABSOLUTE 21 - >> 37 DELETE_FAST 1 (_[1]) - - %-4d 40 LOAD_CONST 2 (1) - 43 BINARY_ADD - 44 RAISE_VARARGS 2 - >> 47 POP_TOP - - %-4d 48 LOAD_CONST 0 (None) - 51 RETURN_VALUE + 13 LOAD_FAST 0 (x) + 16 GET_ITER + >> 17 FOR_ITER 12 (to 32) + 20 STORE_FAST 1 (s) + 23 LOAD_FAST 1 (s) + 26 LIST_APPEND 2 + 29 JUMP_ABSOLUTE 17 + + %-4d >> 32 LOAD_CONST 2 (1) + 35 BINARY_ADD + 36 RAISE_VARARGS 2 + >> 39 POP_TOP + + %-4d 40 LOAD_CONST 0 (None) + 43 RETURN_VALUE """%(bug1333982.__code__.co_firstlineno + 1, bug1333982.__code__.co_firstlineno + 2, bug1333982.__code__.co_firstlineno + 3) diff --git a/Misc/NEWS b/Misc/NEWS index f812ce4..eb9a2bf 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 3.1 alpha 0 Core and Builtins ----------------- +- Issue #2183: Simplify and optimize bytecode for list, dict and set + comprehensions. Original patch for list comprehensions by Neal Norwitz. + - Issue #2467: gc.DEBUG_STATS reported invalid elapsed times. Also, always print elapsed times, not only when some objects are uncollectable / unreachable. Original patch by Neil Schemenauer. diff --git a/Python/ceval.c b/Python/ceval.c index b17d3db..2ce3ec9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1306,9 +1306,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) case LIST_APPEND: w = POP(); - v = POP(); + v = stack_pointer[-oparg]; err = PyList_Append(v, w); - Py_DECREF(v); Py_DECREF(w); if (err == 0) { PREDICT(JUMP_ABSOLUTE); @@ -1318,9 +1317,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) case SET_ADD: w = POP(); - v = POP(); + v = stack_pointer[-oparg]; err = PySet_Add(v, w); - Py_DECREF(v); Py_DECREF(w); if (err == 0) { PREDICT(JUMP_ABSOLUTE); @@ -1935,6 +1933,21 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (err == 0) continue; break; + case MAP_ADD: + w = TOP(); /* key */ + u = SECOND(); /* value */ + STACKADJ(-2); + v = stack_pointer[-oparg]; /* dict */ + assert (PyDict_CheckExact(v)); + err = PyDict_SetItem(v, w, u); /* v[w] = u */ + Py_DECREF(u); + Py_DECREF(w); + if (err == 0) { + PREDICT(JUMP_ABSOLUTE); + continue; + } + break; + case LOAD_ATTR: w = GETITEM(names, oparg); v = TOP(); diff --git a/Python/compile.c b/Python/compile.c index 50eb169..36f8c13 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -707,6 +707,8 @@ opcode_stack_effect(int opcode, int oparg) case SET_ADD: case LIST_APPEND: + return -1; + case MAP_ADD: return -2; case BINARY_POWER: @@ -2823,7 +2825,7 @@ compiler_call_helper(struct compiler *c, */ static int -compiler_comprehension_generator(struct compiler *c, PyObject *tmpname, +compiler_comprehension_generator(struct compiler *c, asdl_seq *generators, int gen_index, expr_ty elt, expr_ty val, int type) { @@ -2871,7 +2873,7 @@ compiler_comprehension_generator(struct compiler *c, PyObject *tmpname, } if (++gen_index < asdl_seq_LEN(generators)) - if (!compiler_comprehension_generator(c, tmpname, + if (!compiler_comprehension_generator(c, generators, gen_index, elt, val, type)) return 0; @@ -2886,27 +2888,19 @@ compiler_comprehension_generator(struct compiler *c, PyObject *tmpname, ADDOP(c, POP_TOP); break; case COMP_LISTCOMP: - if (!compiler_nameop(c, tmpname, Load)) - return 0; VISIT(c, expr, elt); - ADDOP(c, LIST_APPEND); + ADDOP_I(c, LIST_APPEND, gen_index + 1); break; case COMP_SETCOMP: - if (!compiler_nameop(c, tmpname, Load)) - return 0; VISIT(c, expr, elt); - ADDOP(c, SET_ADD); + ADDOP_I(c, SET_ADD, gen_index + 1); break; case COMP_DICTCOMP: - if (!compiler_nameop(c, tmpname, Load)) - return 0; /* With 'd[k] = v', v is evaluated before k, so we do - the same. STORE_SUBSCR requires (item, map, key), - so we still end up ROTing once. */ + the same. */ VISIT(c, expr, val); - ADDOP(c, ROT_TWO); VISIT(c, expr, elt); - ADDOP(c, STORE_SUBSCR); + ADDOP_I(c, MAP_ADD, gen_index + 1); break; default: return 0; @@ -2932,7 +2926,6 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, asdl_seq *generators, expr_ty elt, expr_ty val) { PyCodeObject *co = NULL; - identifier tmp = NULL; expr_ty outermost_iter; outermost_iter = ((comprehension_ty) @@ -2943,9 +2936,6 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, if (type != COMP_GENEXP) { int op; - tmp = compiler_new_tmpname(c); - if (!tmp) - goto error_in_scope; switch (type) { case COMP_LISTCOMP: op = BUILD_LIST; @@ -2963,12 +2953,9 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, } ADDOP_I(c, op, 0); - ADDOP(c, DUP_TOP); - if (!compiler_nameop(c, tmp, Store)) - goto error_in_scope; } - if (!compiler_comprehension_generator(c, tmp, generators, 0, elt, + if (!compiler_comprehension_generator(c, generators, 0, elt, val, type)) goto error_in_scope; @@ -2984,7 +2971,6 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, if (!compiler_make_closure(c, co, 0)) goto error; Py_DECREF(co); - Py_XDECREF(tmp); VISIT(c, expr, outermost_iter); ADDOP(c, GET_ITER); @@ -2994,7 +2980,6 @@ error_in_scope: compiler_exit_scope(c); error: Py_XDECREF(co); - Py_XDECREF(tmp); return 0; } diff --git a/Python/import.c b/Python/import.c index 2bad2e5..621284e 100644 --- a/Python/import.c +++ b/Python/import.c @@ -87,8 +87,10 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *); 3102 (__file__ points to source file) Python 3.0a4: 3110 (WITH_CLEANUP optimization). Python 3.0a5: 3130 (lexical exception stacking, including POP_EXCEPT) + Python 3.1a0: 3140 (optimize list, set and dict comprehensions: + change LIST_APPEND and SET_ADD, add MAP_ADD) */ -#define MAGIC (3130 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (3140 | ((long)'\r'<<16) | ((long)'\n'<<24)) /* Magic word as global; note that _PyImport_Init() can change the value of this global to accommodate for alterations of how the -- cgit v0.12