Skip to content

Commit 8773653

Browse files
committed
clear comp locals on entry, eval iter expr first
1 parent 4620856 commit 8773653

File tree

9 files changed

+106
-76
lines changed

9 files changed

+106
-76
lines changed

Doc/library/dis.rst

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,13 +1213,11 @@ iterations of the loop.
12131213

12141214
.. versionadded:: 3.12
12151215

1216-
.. opcode:: LOAD_FAST_OR_NULL (var_num)
1216+
.. opcode:: LOAD_FAST_AND_CLEAR (var_num)
12171217

1218-
Pushes a reference to the local ``co_varnames[var_num]`` onto the stack, or
1218+
Pushes a reference to the local ``co_varnames[var_num]`` onto the stack (or
12191219
pushes ``NULL`` onto the stack if the local variable has not been
1220-
initialized. This opcode has the same runtime effect as ``LOAD_FAST``; it
1221-
exists to maintain the invariant that ``LOAD_FAST`` will never load ``NULL``
1222-
and may appear only where the variable is guaranteed to be initialized.
1220+
initialized) and sets ``co_varnames[var_num]`` to ``NULL``.
12231221

12241222
.. versionadded:: 3.12
12251223

Include/internal/pycore_opcode.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/opcode.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/opcode.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ def pseudo_op(name, op, real_ops):
196196
hascompare.append(141)
197197

198198
def_op('CALL_FUNCTION_EX', 142) # Flags
199-
def_op('LOAD_FAST_OR_NULL', 143) # Local variable number, may load NULL if undefined
199+
def_op('LOAD_FAST_AND_CLEAR', 143) # Local variable number
200200
haslocal.append(143)
201201

202202
def_op('EXTENDED_ARG', 144)

Python/bytecodes.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,10 @@ dummy_func(
114114
Py_INCREF(value);
115115
}
116116

117-
inst(LOAD_FAST_OR_NULL, (-- value)) {
117+
inst(LOAD_FAST_AND_CLEAR, (-- value)) {
118118
value = GETLOCAL(oparg);
119-
Py_XINCREF(value);
119+
// do not use SETLOCAL here, it decrefs the old value
120+
GETLOCAL(oparg) = NULL;
120121
}
121122

122123
inst(LOAD_CONST, (-- value)) {

Python/compile.c

Lines changed: 89 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -550,14 +550,14 @@ static int compiler_sync_comprehension_generator(
550550
asdl_comprehension_seq *generators, int gen_index,
551551
int depth,
552552
expr_ty elt, expr_ty val, int type,
553-
int outermost_iter_is_param);
553+
int iter_on_stack);
554554

555555
static int compiler_async_comprehension_generator(
556556
struct compiler *c, location loc,
557557
asdl_comprehension_seq *generators, int gen_index,
558558
int depth,
559559
expr_ty elt, expr_ty val, int type,
560-
int outermost_iter_is_param);
560+
int iter_on_stack);
561561

562562
static int compiler_pattern(struct compiler *, pattern_ty, pattern_context *);
563563
static int compiler_match(struct compiler *, stmt_ty);
@@ -1229,7 +1229,7 @@ stack_effect(int opcode, int oparg, int jump)
12291229

12301230
case LOAD_FAST:
12311231
case LOAD_FAST_CHECK:
1232-
case LOAD_FAST_OR_NULL:
1232+
case LOAD_FAST_AND_CLEAR:
12331233
return 1;
12341234
case STORE_FAST:
12351235
case STORE_FAST_MAYBE_NULL:
@@ -5150,18 +5150,18 @@ compiler_comprehension_generator(struct compiler *c, location loc,
51505150
asdl_comprehension_seq *generators, int gen_index,
51515151
int depth,
51525152
expr_ty elt, expr_ty val, int type,
5153-
int outermost_iter_is_param)
5153+
int iter_on_stack)
51545154
{
51555155
comprehension_ty gen;
51565156
gen = (comprehension_ty)asdl_seq_GET(generators, gen_index);
51575157
if (gen->is_async) {
51585158
return compiler_async_comprehension_generator(
51595159
c, loc, generators, gen_index, depth, elt, val, type,
5160-
outermost_iter_is_param);
5160+
iter_on_stack);
51615161
} else {
51625162
return compiler_sync_comprehension_generator(
51635163
c, loc, generators, gen_index, depth, elt, val, type,
5164-
outermost_iter_is_param);
5164+
iter_on_stack);
51655165
}
51665166
}
51675167

@@ -5170,7 +5170,7 @@ compiler_sync_comprehension_generator(struct compiler *c, location loc,
51705170
asdl_comprehension_seq *generators,
51715171
int gen_index, int depth,
51725172
expr_ty elt, expr_ty val, int type,
5173-
int outermost_iter_is_param)
5173+
int iter_on_stack)
51745174
{
51755175
/* generate code for the iterator, then each of the ifs,
51765176
and then write to the element */
@@ -5182,37 +5182,39 @@ compiler_sync_comprehension_generator(struct compiler *c, location loc,
51825182
comprehension_ty gen = (comprehension_ty)asdl_seq_GET(generators,
51835183
gen_index);
51845184

5185-
if (gen_index == 0 && outermost_iter_is_param) {
5186-
/* Receive outermost iter as an implicit argument */
5187-
c->u->u_argcount = 1;
5188-
ADDOP_I(c, loc, LOAD_FAST, 0);
5189-
}
5190-
else {
5191-
/* Sub-iter - calculate on the fly */
5192-
/* Fast path for the temporary variable assignment idiom:
5193-
for y in [f(x)]
5194-
*/
5195-
asdl_expr_seq *elts;
5196-
switch (gen->iter->kind) {
5197-
case List_kind:
5198-
elts = gen->iter->v.List.elts;
5199-
break;
5200-
case Tuple_kind:
5201-
elts = gen->iter->v.Tuple.elts;
5202-
break;
5203-
default:
5204-
elts = NULL;
5185+
if (!iter_on_stack) {
5186+
if (gen_index == 0) {
5187+
/* Receive outermost iter as an implicit argument */
5188+
c->u->u_argcount = 1;
5189+
ADDOP_I(c, loc, LOAD_FAST, 0);
52055190
}
5206-
if (asdl_seq_LEN(elts) == 1) {
5207-
expr_ty elt = asdl_seq_GET(elts, 0);
5208-
if (elt->kind != Starred_kind) {
5209-
VISIT(c, expr, elt);
5210-
start = NO_LABEL;
5191+
else {
5192+
/* Sub-iter - calculate on the fly */
5193+
/* Fast path for the temporary variable assignment idiom:
5194+
for y in [f(x)]
5195+
*/
5196+
asdl_expr_seq *elts;
5197+
switch (gen->iter->kind) {
5198+
case List_kind:
5199+
elts = gen->iter->v.List.elts;
5200+
break;
5201+
case Tuple_kind:
5202+
elts = gen->iter->v.Tuple.elts;
5203+
break;
5204+
default:
5205+
elts = NULL;
5206+
}
5207+
if (asdl_seq_LEN(elts) == 1) {
5208+
expr_ty elt = asdl_seq_GET(elts, 0);
5209+
if (elt->kind != Starred_kind) {
5210+
VISIT(c, expr, elt);
5211+
start = NO_LABEL;
5212+
}
5213+
}
5214+
if (IS_LABEL(start)) {
5215+
VISIT(c, expr, gen->iter);
5216+
ADDOP(c, loc, GET_ITER);
52115217
}
5212-
}
5213-
if (IS_LABEL(start)) {
5214-
VISIT(c, expr, gen->iter);
5215-
ADDOP(c, loc, GET_ITER);
52165218
}
52175219
}
52185220
if (IS_LABEL(start)) {
@@ -5287,7 +5289,7 @@ compiler_async_comprehension_generator(struct compiler *c, location loc,
52875289
asdl_comprehension_seq *generators,
52885290
int gen_index, int depth,
52895291
expr_ty elt, expr_ty val, int type,
5290-
int outermost_iter_is_param)
5292+
int iter_on_stack)
52915293
{
52925294
NEW_JUMP_TARGET_LABEL(c, start);
52935295
NEW_JUMP_TARGET_LABEL(c, except);
@@ -5296,15 +5298,17 @@ compiler_async_comprehension_generator(struct compiler *c, location loc,
52965298
comprehension_ty gen = (comprehension_ty)asdl_seq_GET(generators,
52975299
gen_index);
52985300

5299-
if (gen_index == 0 && outermost_iter_is_param) {
5300-
/* Receive outermost iter as an implicit argument */
5301-
c->u->u_argcount = 1;
5302-
ADDOP_I(c, loc, LOAD_FAST, 0);
5303-
}
5304-
else {
5305-
/* Sub-iter - calculate on the fly */
5306-
VISIT(c, expr, gen->iter);
5307-
ADDOP(c, loc, GET_AITER);
5301+
if (!iter_on_stack) {
5302+
if (gen_index == 0) {
5303+
/* Receive outermost iter as an implicit argument */
5304+
c->u->u_argcount = 1;
5305+
ADDOP_I(c, loc, LOAD_FAST, 0);
5306+
}
5307+
else {
5308+
/* Sub-iter - calculate on the fly */
5309+
VISIT(c, expr, gen->iter);
5310+
ADDOP(c, loc, GET_AITER);
5311+
}
53085312
}
53095313

53105314
USE_LABEL(c, start);
@@ -5449,7 +5453,7 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
54495453
// in the case of a cell, this will actually push the cell
54505454
// itself to the stack, then we'll create a new one for the
54515455
// comprehension and restore the original one after
5452-
ADDOP_NAME(c, loc, LOAD_FAST_OR_NULL, k, varnames);
5456+
ADDOP_NAME(c, loc, LOAD_FAST_AND_CLEAR, k, varnames);
54535457
if (scope == CELL) {
54545458
ADDOP_NAME(c, loc, MAKE_CELL, k, cellvars);
54555459
}
@@ -5459,6 +5463,14 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
54595463
}
54605464
}
54615465
}
5466+
if (state->pushed_locals) {
5467+
// Outermost iterable expression was already evaluated and is on the
5468+
// stack, we need to swap it back to TOS. This also rotates the order of
5469+
// `pushed_locals` on the stack, but this will be reversed when we swap
5470+
// out the comprehension result in pop_inlined_comprehension_state
5471+
ADDOP_I(c, loc, SWAP, PyList_GET_SIZE(state->pushed_locals) + 1);
5472+
}
5473+
54625474
return SUCCESS;
54635475
}
54645476

@@ -5479,11 +5491,13 @@ pop_inlined_comprehension_state(struct compiler *c, location loc,
54795491
if (state.pushed_locals) {
54805492
// pop names we pushed to stack earlier
54815493
Py_ssize_t npops = PyList_GET_SIZE(state.pushed_locals);
5482-
// preserve the list/dict/set result of the comprehension as TOS
5494+
// Preserve the list/dict/set result of the comprehension as TOS. This
5495+
// reverses the SWAP we did in push_inlined_comprehension_state to get
5496+
// the outermost iterable to TOS, so we can still just iterate
5497+
// pushed_locals in simple reverse order
54835498
ADDOP_I(c, loc, SWAP, npops + 1);
5484-
for (Py_ssize_t i = npops; i > 0; --i) {
5485-
// i % npops: pop in order e.g. 0, 3, 2, 1: accounts for the swap
5486-
k = PyList_GetItem(state.pushed_locals, i % npops);
5499+
for (Py_ssize_t i = npops - 1; i >= 0; --i) {
5500+
k = PyList_GetItem(state.pushed_locals, i);
54875501
if (k == NULL) {
54885502
return ERROR;
54895503
}
@@ -5493,6 +5507,19 @@ pop_inlined_comprehension_state(struct compiler *c, location loc,
54935507
return SUCCESS;
54945508
}
54955509

5510+
static inline int
5511+
compiler_comprehension_iter(struct compiler *c, location loc,
5512+
comprehension_ty comp)
5513+
{
5514+
VISIT(c, expr, comp->iter);
5515+
if (comp->is_async) {
5516+
ADDOP(c, loc, GET_AITER);
5517+
} else {
5518+
ADDOP(c, loc, GET_ITER);
5519+
}
5520+
return SUCCESS;
5521+
}
5522+
54965523
static int
54975524
compiler_comprehension(struct compiler *c, expr_ty e, int type,
54985525
identifier name, asdl_comprehension_seq *generators, expr_ty elt,
@@ -5516,6 +5543,9 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
55165543

55175544
outermost = (comprehension_ty) asdl_seq_GET(generators, 0);
55185545
if (is_inlined) {
5546+
if (compiler_comprehension_iter(c, loc, outermost)) {
5547+
goto error;
5548+
}
55195549
if (push_inlined_comprehension_state(c, loc, entry, &inline_state)) {
55205550
goto error;
55215551
}
@@ -5557,10 +5587,13 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
55575587
}
55585588

55595589
ADDOP_I(c, loc, op, 0);
5590+
if (is_inlined) {
5591+
ADDOP_I(c, loc, SWAP, 2);
5592+
}
55605593
}
55615594

55625595
if (compiler_comprehension_generator(c, loc, generators, 0, 0,
5563-
elt, val, type, !is_inlined) < 0) {
5596+
elt, val, type, is_inlined) < 0) {
55645597
goto error_in_scope;
55655598
}
55665599

@@ -5595,13 +5628,8 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
55955628
}
55965629
Py_DECREF(co);
55975630

5598-
VISIT(c, expr, outermost->iter);
5599-
5600-
loc = LOC(e);
5601-
if (outermost->is_async) {
5602-
ADDOP(c, loc, GET_AITER);
5603-
} else {
5604-
ADDOP(c, loc, GET_ITER);
5631+
if (compiler_comprehension_iter(c, loc, outermost)) {
5632+
goto error;
56055633
}
56065634

56075635
ADDOP_I(c, loc, CALL, 0);
@@ -8141,6 +8169,7 @@ scan_block_for_locals(basicblock *b, basicblock ***sp)
81418169
uint64_t bit = (uint64_t)1 << instr->i_oparg;
81428170
switch (instr->i_opcode) {
81438171
case DELETE_FAST:
8172+
case LOAD_FAST_AND_CLEAR:
81448173
unsafe_mask |= bit;
81458174
break;
81468175
case STORE_FAST:
@@ -8194,6 +8223,7 @@ fast_scan_many_locals(basicblock *entryblock, int nlocals)
81948223
assert(arg >= 0);
81958224
switch (instr->i_opcode) {
81968225
case DELETE_FAST:
8226+
case LOAD_FAST_AND_CLEAR:
81978227
states[arg - 64] = blocknum - 1;
81988228
break;
81998229
case STORE_FAST:

Python/generated_cases.c.h

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/opcode_metadata.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
1616
return 0;
1717
case LOAD_FAST:
1818
return 0;
19-
case LOAD_FAST_OR_NULL:
19+
case LOAD_FAST_AND_CLEAR:
2020
return 0;
2121
case LOAD_CONST:
2222
return 0;
@@ -364,7 +364,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
364364
return 1;
365365
case LOAD_FAST:
366366
return 1;
367-
case LOAD_FAST_OR_NULL:
367+
case LOAD_FAST_AND_CLEAR:
368368
return 1;
369369
case LOAD_CONST:
370370
return 1;
@@ -711,7 +711,7 @@ struct opcode_metadata {
711711
[LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
712712
[LOAD_FAST_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
713713
[LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
714-
[LOAD_FAST_OR_NULL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
714+
[LOAD_FAST_AND_CLEAR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
715715
[LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
716716
[STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
717717
[LOAD_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },

Python/opcode_targets.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)