Skip to content

Commit 25e5a9f

Browse files
author
antoine.pitrou
committed
Issue #2183: Simplify and optimize bytecode for list comprehensions.
git-svn-id: http://svn.python.org/projects/python/trunk@67818 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent a937076 commit 25e5a9f

File tree

9 files changed

+36
-65
lines changed

9 files changed

+36
-65
lines changed

Doc/library/dis.rst

+4-2
Original file line numberDiff line numberDiff line change
@@ -463,9 +463,11 @@ Miscellaneous opcodes.
463463
address to jump to (which should be a ``FOR_ITER`` instruction).
464464

465465

466-
.. opcode:: LIST_APPEND ()
466+
.. opcode:: LIST_APPEND (i)
467467

468-
Calls ``list.append(TOS1, TOS)``. Used to implement list comprehensions.
468+
Calls ``list.append(TOS[-i], TOS)``. Used to implement list comprehensions.
469+
While the appended value is popped off, the list object remains on the
470+
stack so that it is available for further iterations of the loop.
469471

470472

471473
.. opcode:: LOAD_LOCALS ()

Include/opcode.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ extern "C" {
2222

2323
#define UNARY_INVERT 15
2424

25-
#define LIST_APPEND 18
2625
#define BINARY_POWER 19
2726

2827
#define BINARY_MULTIPLY 20
@@ -89,6 +88,7 @@ extern "C" {
8988
#define DELETE_NAME 91 /* "" */
9089
#define UNPACK_SEQUENCE 92 /* Number of sequence items */
9190
#define FOR_ITER 93
91+
#define LIST_APPEND 94
9292

9393
#define STORE_ATTR 95 /* Index in name list */
9494
#define DELETE_ATTR 96 /* "" */

Lib/compiler/pycodegen.py

+1-11
Original file line numberDiff line numberDiff line change
@@ -570,16 +570,10 @@ def visitCompare(self, node):
570570
self.nextBlock(end)
571571

572572
# list comprehensions
573-
__list_count = 0
574-
575573
def visitListComp(self, node):
576574
self.set_lineno(node)
577575
# setup list
578-
tmpname = "$list%d" % self.__list_count
579-
self.__list_count = self.__list_count + 1
580576
self.emit('BUILD_LIST', 0)
581-
self.emit('DUP_TOP')
582-
self._implicitNameOp('STORE', tmpname)
583577

584578
stack = []
585579
for i, for_ in zip(range(len(node.quals)), node.quals):
@@ -591,9 +585,8 @@ def visitListComp(self, node):
591585
self.visit(if_, cont)
592586
stack.insert(0, (start, cont, anchor))
593587

594-
self._implicitNameOp('LOAD', tmpname)
595588
self.visit(node.expr)
596-
self.emit('LIST_APPEND')
589+
self.emit('LIST_APPEND', len(node.quals) + 1)
597590

598591
for start, cont, anchor in stack:
599592
if cont:
@@ -604,9 +597,6 @@ def visitListComp(self, node):
604597
self.nextBlock(skip_one)
605598
self.emit('JUMP_ABSOLUTE', start)
606599
self.startBlock(anchor)
607-
self._implicitNameOp('DELETE', tmpname)
608-
609-
self.__list_count = self.__list_count - 1
610600

611601
def visitListCompFor(self, node):
612602
start = self.newBlock()

Lib/opcode.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ def jabs_op(name, op):
5858

5959
def_op('UNARY_INVERT', 15)
6060

61-
def_op('LIST_APPEND', 18)
6261
def_op('BINARY_POWER', 19)
6362
def_op('BINARY_MULTIPLY', 20)
6463
def_op('BINARY_DIVIDE', 21)
@@ -128,7 +127,7 @@ def jabs_op(name, op):
128127
name_op('DELETE_NAME', 91) # ""
129128
def_op('UNPACK_SEQUENCE', 92) # Number of tuple items
130129
jrel_op('FOR_ITER', 93)
131-
130+
def_op('LIST_APPEND', 94)
132131
name_op('STORE_ATTR', 95) # Index in name list
133132
name_op('DELETE_ATTR', 96) # ""
134133
name_op('STORE_GLOBAL', 97) # ""

Lib/test/test_dis.py

+16-20
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,25 @@ def bug1333982(x=[]):
5454

5555
dis_bug1333982 = """\
5656
%-4d 0 LOAD_CONST 1 (0)
57-
3 JUMP_IF_TRUE 41 (to 47)
57+
3 JUMP_IF_TRUE 33 (to 39)
5858
6 POP_TOP
5959
7 LOAD_GLOBAL 0 (AssertionError)
6060
10 BUILD_LIST 0
61-
13 DUP_TOP
62-
14 STORE_FAST 1 (_[1])
63-
17 LOAD_FAST 0 (x)
64-
20 GET_ITER
65-
>> 21 FOR_ITER 13 (to 37)
66-
24 STORE_FAST 2 (s)
67-
27 LOAD_FAST 1 (_[1])
68-
30 LOAD_FAST 2 (s)
69-
33 LIST_APPEND
70-
34 JUMP_ABSOLUTE 21
71-
>> 37 DELETE_FAST 1 (_[1])
72-
73-
%-4d 40 LOAD_CONST 2 (1)
74-
43 BINARY_ADD
75-
44 RAISE_VARARGS 2
76-
>> 47 POP_TOP
77-
78-
%-4d 48 LOAD_CONST 0 (None)
79-
51 RETURN_VALUE
61+
13 LOAD_FAST 0 (x)
62+
16 GET_ITER
63+
>> 17 FOR_ITER 12 (to 32)
64+
20 STORE_FAST 1 (s)
65+
23 LOAD_FAST 1 (s)
66+
26 LIST_APPEND 2
67+
29 JUMP_ABSOLUTE 17
68+
69+
%-4d >> 32 LOAD_CONST 2 (1)
70+
35 BINARY_ADD
71+
36 RAISE_VARARGS 2
72+
>> 39 POP_TOP
73+
74+
%-4d 40 LOAD_CONST 0 (None)
75+
43 RETURN_VALUE
8076
"""%(bug1333982.func_code.co_firstlineno + 1,
8177
bug1333982.func_code.co_firstlineno + 2,
8278
bug1333982.func_code.co_firstlineno + 3)

Misc/NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in Python 2.7 alpha 1
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #2183: Simplify and optimize bytecode for list comprehensions.
16+
Original patch by Neal Norwitz.
17+
1518
- Issue #4597: Fixed exception handling when the __exit__ function of a
1619
context manager returns a value that cannot be converted to a bool.
1720

Python/ceval.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -1294,9 +1294,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
12941294

12951295
case LIST_APPEND:
12961296
w = POP();
1297-
v = POP();
1297+
v = stack_pointer[-oparg];
12981298
err = PyList_Append(v, w);
1299-
Py_DECREF(v);
13001299
Py_DECREF(w);
13011300
if (err == 0) {
13021301
PREDICT(JUMP_ABSOLUTE);

Python/compile.c

+7-26
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ opcode_stack_effect(int opcode, int oparg)
693693
return 0;
694694

695695
case LIST_APPEND:
696-
return -2;
696+
return -1;
697697

698698
case BINARY_POWER:
699699
case BINARY_MULTIPLY:
@@ -2599,9 +2599,8 @@ compiler_call(struct compiler *c, expr_ty e)
25992599
}
26002600

26012601
static int
2602-
compiler_listcomp_generator(struct compiler *c, PyObject *tmpname,
2603-
asdl_seq *generators, int gen_index,
2604-
expr_ty elt)
2602+
compiler_listcomp_generator(struct compiler *c, asdl_seq *generators,
2603+
int gen_index, expr_ty elt)
26052604
{
26062605
/* generate code for the iterator, then each of the ifs,
26072606
and then write to the element */
@@ -2638,16 +2637,13 @@ compiler_listcomp_generator(struct compiler *c, PyObject *tmpname,
26382637
}
26392638

26402639
if (++gen_index < asdl_seq_LEN(generators))
2641-
if (!compiler_listcomp_generator(c, tmpname,
2642-
generators, gen_index, elt))
2640+
if (!compiler_listcomp_generator(c, generators, gen_index, elt))
26432641
return 0;
26442642

26452643
/* only append after the last for generator */
26462644
if (gen_index >= asdl_seq_LEN(generators)) {
2647-
if (!compiler_nameop(c, tmpname, Load))
2648-
return 0;
26492645
VISIT(c, expr, elt);
2650-
ADDOP(c, LIST_APPEND);
2646+
ADDOP_I(c, LIST_APPEND, gen_index+1);
26512647

26522648
compiler_use_next_block(c, skip);
26532649
}
@@ -2659,32 +2655,17 @@ compiler_listcomp_generator(struct compiler *c, PyObject *tmpname,
26592655
}
26602656
ADDOP_JABS(c, JUMP_ABSOLUTE, start);
26612657
compiler_use_next_block(c, anchor);
2662-
/* delete the temporary list name added to locals */
2663-
if (gen_index == 1)
2664-
if (!compiler_nameop(c, tmpname, Del))
2665-
return 0;
26662658

26672659
return 1;
26682660
}
26692661

26702662
static int
26712663
compiler_listcomp(struct compiler *c, expr_ty e)
26722664
{
2673-
identifier tmp;
2674-
int rc = 0;
2675-
asdl_seq *generators = e->v.ListComp.generators;
2676-
26772665
assert(e->kind == ListComp_kind);
2678-
tmp = compiler_new_tmpname(c);
2679-
if (!tmp)
2680-
return 0;
26812666
ADDOP_I(c, BUILD_LIST, 0);
2682-
ADDOP(c, DUP_TOP);
2683-
if (compiler_nameop(c, tmp, Store))
2684-
rc = compiler_listcomp_generator(c, tmp, generators, 0,
2685-
e->v.ListComp.elt);
2686-
Py_DECREF(tmp);
2687-
return rc;
2667+
return compiler_listcomp_generator(c, e->v.ListComp.generators, 0,
2668+
e->v.ListComp.elt);
26882669
}
26892670

26902671
static int

Python/import.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,10 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
7373
Python 2.5c2: 62131 (fix wrong code: for x, in ... in listcomp/genexp)
7474
Python 2.6a0: 62151 (peephole optimizations and STORE_MAP opcode)
7575
Python 2.6a1: 62161 (WITH_CLEANUP optimization)
76+
Python 2.7a0: 62171 (optimize list comprehensions/change LIST_APPEND)
7677
.
7778
*/
78-
#define MAGIC (62161 | ((long)'\r'<<16) | ((long)'\n'<<24))
79+
#define MAGIC (62171 | ((long)'\r'<<16) | ((long)'\n'<<24))
7980

8081
/* Magic word as global; note that _PyImport_Init() can change the
8182
value of this global to accommodate for alterations of how the

0 commit comments

Comments
 (0)