diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index e3ce47150bfa4d..2c9d868ebd872b 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -1213,13 +1213,11 @@ iterations of the loop. .. versionadded:: 3.12 -.. opcode:: LOAD_FAST_OR_NULL (var_num) +.. opcode:: LOAD_FAST_AND_CLEAR (var_num) - Pushes a reference to the local ``co_varnames[var_num]`` onto the stack, or + Pushes a reference to the local ``co_varnames[var_num]`` onto the stack (or pushes ``NULL`` onto the stack if the local variable has not been - initialized. This opcode has the same runtime effect as ``LOAD_FAST``; it - exists to maintain the invariant that ``LOAD_FAST`` will never load ``NULL`` - and may appear only where the variable is guaranteed to be initialized. + initialized) and sets ``co_varnames[var_num]`` to ``NULL``. .. versionadded:: 3.12 diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index a54aea42afc1d1..2a86c6b24d4b21 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -165,8 +165,8 @@ const uint8_t _PyOpcode_Deopt[256] = { [LOAD_CONST__LOAD_FAST] = LOAD_CONST, [LOAD_DEREF] = LOAD_DEREF, [LOAD_FAST] = LOAD_FAST, + [LOAD_FAST_AND_CLEAR] = LOAD_FAST_AND_CLEAR, [LOAD_FAST_CHECK] = LOAD_FAST_CHECK, - [LOAD_FAST_OR_NULL] = LOAD_FAST_OR_NULL, [LOAD_FAST__LOAD_CONST] = LOAD_FAST, [LOAD_FAST__LOAD_FAST] = LOAD_FAST, [LOAD_GLOBAL] = LOAD_GLOBAL, @@ -372,7 +372,7 @@ static const char *const _PyOpcode_OpName[264] = { [JUMP_BACKWARD] = "JUMP_BACKWARD", [COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [LOAD_FAST_OR_NULL] = "LOAD_FAST_OR_NULL", + [LOAD_FAST_AND_CLEAR] = "LOAD_FAST_AND_CLEAR", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", diff --git a/Include/opcode.h b/Include/opcode.h index 2dbefb5cdf6342..9e0cc18c4345ea 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -97,7 +97,7 @@ extern "C" { #define JUMP_BACKWARD 140 #define COMPARE_AND_BRANCH 141 #define CALL_FUNCTION_EX 142 -#define LOAD_FAST_OR_NULL 143 +#define LOAD_FAST_AND_CLEAR 143 #define EXTENDED_ARG 144 #define LIST_APPEND 145 #define SET_ADD 146 diff --git a/Lib/opcode.py b/Lib/opcode.py index 1846d19739afe4..bc5cedef8ce197 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -196,7 +196,7 @@ def pseudo_op(name, op, real_ops): hascompare.append(141) def_op('CALL_FUNCTION_EX', 142) # Flags -def_op('LOAD_FAST_OR_NULL', 143) # Local variable number, may load NULL if undefined +def_op('LOAD_FAST_AND_CLEAR', 143) # Local variable number haslocal.append(143) def_op('EXTENDED_ARG', 144) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c468e7cffbded8..4e8c6037909aa0 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -114,9 +114,10 @@ dummy_func( Py_INCREF(value); } - inst(LOAD_FAST_OR_NULL, (-- value)) { + inst(LOAD_FAST_AND_CLEAR, (-- value)) { value = GETLOCAL(oparg); - Py_XINCREF(value); + // do not use SETLOCAL here, it decrefs the old value + GETLOCAL(oparg) = NULL; } inst(LOAD_CONST, (-- value)) { diff --git a/Python/compile.c b/Python/compile.c index bc60931c07b9e6..5787155dbb7166 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -550,14 +550,14 @@ static int compiler_sync_comprehension_generator( asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int outermost_iter_is_param); + int iter_on_stack); static int compiler_async_comprehension_generator( struct compiler *c, location loc, asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int outermost_iter_is_param); + int iter_on_stack); static int compiler_pattern(struct compiler *, pattern_ty, pattern_context *); static int compiler_match(struct compiler *, stmt_ty); @@ -1229,7 +1229,7 @@ stack_effect(int opcode, int oparg, int jump) case LOAD_FAST: case LOAD_FAST_CHECK: - case LOAD_FAST_OR_NULL: + case LOAD_FAST_AND_CLEAR: return 1; case STORE_FAST: case STORE_FAST_MAYBE_NULL: @@ -5150,18 +5150,18 @@ compiler_comprehension_generator(struct compiler *c, location loc, asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int outermost_iter_is_param) + int iter_on_stack) { comprehension_ty gen; gen = (comprehension_ty)asdl_seq_GET(generators, gen_index); if (gen->is_async) { return compiler_async_comprehension_generator( c, loc, generators, gen_index, depth, elt, val, type, - outermost_iter_is_param); + iter_on_stack); } else { return compiler_sync_comprehension_generator( c, loc, generators, gen_index, depth, elt, val, type, - outermost_iter_is_param); + iter_on_stack); } } @@ -5170,7 +5170,7 @@ compiler_sync_comprehension_generator(struct compiler *c, location loc, asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int outermost_iter_is_param) + int iter_on_stack) { /* generate code for the iterator, then each of the ifs, and then write to the element */ @@ -5182,37 +5182,39 @@ compiler_sync_comprehension_generator(struct compiler *c, location loc, comprehension_ty gen = (comprehension_ty)asdl_seq_GET(generators, gen_index); - if (gen_index == 0 && outermost_iter_is_param) { - /* Receive outermost iter as an implicit argument */ - c->u->u_argcount = 1; - ADDOP_I(c, loc, LOAD_FAST, 0); - } - else { - /* Sub-iter - calculate on the fly */ - /* Fast path for the temporary variable assignment idiom: - for y in [f(x)] - */ - asdl_expr_seq *elts; - switch (gen->iter->kind) { - case List_kind: - elts = gen->iter->v.List.elts; - break; - case Tuple_kind: - elts = gen->iter->v.Tuple.elts; - break; - default: - elts = NULL; + if (!iter_on_stack) { + if (gen_index == 0) { + /* Receive outermost iter as an implicit argument */ + c->u->u_argcount = 1; + ADDOP_I(c, loc, LOAD_FAST, 0); } - if (asdl_seq_LEN(elts) == 1) { - expr_ty elt = asdl_seq_GET(elts, 0); - if (elt->kind != Starred_kind) { - VISIT(c, expr, elt); - start = NO_LABEL; + else { + /* Sub-iter - calculate on the fly */ + /* Fast path for the temporary variable assignment idiom: + for y in [f(x)] + */ + asdl_expr_seq *elts; + switch (gen->iter->kind) { + case List_kind: + elts = gen->iter->v.List.elts; + break; + case Tuple_kind: + elts = gen->iter->v.Tuple.elts; + break; + default: + elts = NULL; + } + if (asdl_seq_LEN(elts) == 1) { + expr_ty elt = asdl_seq_GET(elts, 0); + if (elt->kind != Starred_kind) { + VISIT(c, expr, elt); + start = NO_LABEL; + } + } + if (IS_LABEL(start)) { + VISIT(c, expr, gen->iter); + ADDOP(c, loc, GET_ITER); } - } - if (IS_LABEL(start)) { - VISIT(c, expr, gen->iter); - ADDOP(c, loc, GET_ITER); } } if (IS_LABEL(start)) { @@ -5287,7 +5289,7 @@ compiler_async_comprehension_generator(struct compiler *c, location loc, asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int outermost_iter_is_param) + int iter_on_stack) { NEW_JUMP_TARGET_LABEL(c, start); NEW_JUMP_TARGET_LABEL(c, except); @@ -5296,15 +5298,17 @@ compiler_async_comprehension_generator(struct compiler *c, location loc, comprehension_ty gen = (comprehension_ty)asdl_seq_GET(generators, gen_index); - if (gen_index == 0 && outermost_iter_is_param) { - /* Receive outermost iter as an implicit argument */ - c->u->u_argcount = 1; - ADDOP_I(c, loc, LOAD_FAST, 0); - } - else { - /* Sub-iter - calculate on the fly */ - VISIT(c, expr, gen->iter); - ADDOP(c, loc, GET_AITER); + if (!iter_on_stack) { + if (gen_index == 0) { + /* Receive outermost iter as an implicit argument */ + c->u->u_argcount = 1; + ADDOP_I(c, loc, LOAD_FAST, 0); + } + else { + /* Sub-iter - calculate on the fly */ + VISIT(c, expr, gen->iter); + ADDOP(c, loc, GET_AITER); + } } USE_LABEL(c, start); @@ -5449,7 +5453,7 @@ push_inlined_comprehension_state(struct compiler *c, location loc, // in the case of a cell, this will actually push the cell // itself to the stack, then we'll create a new one for the // comprehension and restore the original one after - ADDOP_NAME(c, loc, LOAD_FAST_OR_NULL, k, varnames); + ADDOP_NAME(c, loc, LOAD_FAST_AND_CLEAR, k, varnames); if (scope == CELL) { ADDOP_NAME(c, loc, MAKE_CELL, k, cellvars); } @@ -5459,6 +5463,14 @@ push_inlined_comprehension_state(struct compiler *c, location loc, } } } + if (state->pushed_locals) { + // Outermost iterable expression was already evaluated and is on the + // stack, we need to swap it back to TOS. This also rotates the order of + // `pushed_locals` on the stack, but this will be reversed when we swap + // out the comprehension result in pop_inlined_comprehension_state + ADDOP_I(c, loc, SWAP, PyList_GET_SIZE(state->pushed_locals) + 1); + } + return SUCCESS; } @@ -5479,11 +5491,13 @@ pop_inlined_comprehension_state(struct compiler *c, location loc, if (state.pushed_locals) { // pop names we pushed to stack earlier Py_ssize_t npops = PyList_GET_SIZE(state.pushed_locals); - // preserve the list/dict/set result of the comprehension as TOS + // Preserve the list/dict/set result of the comprehension as TOS. This + // reverses the SWAP we did in push_inlined_comprehension_state to get + // the outermost iterable to TOS, so we can still just iterate + // pushed_locals in simple reverse order ADDOP_I(c, loc, SWAP, npops + 1); - for (Py_ssize_t i = npops; i > 0; --i) { - // i % npops: pop in order e.g. 0, 3, 2, 1: accounts for the swap - k = PyList_GetItem(state.pushed_locals, i % npops); + for (Py_ssize_t i = npops - 1; i >= 0; --i) { + k = PyList_GetItem(state.pushed_locals, i); if (k == NULL) { return ERROR; } @@ -5493,6 +5507,19 @@ pop_inlined_comprehension_state(struct compiler *c, location loc, return SUCCESS; } +static inline int +compiler_comprehension_iter(struct compiler *c, location loc, + comprehension_ty comp) +{ + VISIT(c, expr, comp->iter); + if (comp->is_async) { + ADDOP(c, loc, GET_AITER); + } else { + ADDOP(c, loc, GET_ITER); + } + return SUCCESS; +} + static int compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, asdl_comprehension_seq *generators, expr_ty elt, @@ -5516,6 +5543,9 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, outermost = (comprehension_ty) asdl_seq_GET(generators, 0); if (is_inlined) { + if (compiler_comprehension_iter(c, loc, outermost)) { + goto error; + } if (push_inlined_comprehension_state(c, loc, entry, &inline_state)) { goto error; } @@ -5557,10 +5587,13 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, } ADDOP_I(c, loc, op, 0); + if (is_inlined) { + ADDOP_I(c, loc, SWAP, 2); + } } if (compiler_comprehension_generator(c, loc, generators, 0, 0, - elt, val, type, !is_inlined) < 0) { + elt, val, type, is_inlined) < 0) { goto error_in_scope; } @@ -5595,13 +5628,8 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, } Py_DECREF(co); - VISIT(c, expr, outermost->iter); - - loc = LOC(e); - if (outermost->is_async) { - ADDOP(c, loc, GET_AITER); - } else { - ADDOP(c, loc, GET_ITER); + if (compiler_comprehension_iter(c, loc, outermost)) { + goto error; } ADDOP_I(c, loc, CALL, 0); @@ -8141,6 +8169,7 @@ scan_block_for_locals(basicblock *b, basicblock ***sp) uint64_t bit = (uint64_t)1 << instr->i_oparg; switch (instr->i_opcode) { case DELETE_FAST: + case LOAD_FAST_AND_CLEAR: unsafe_mask |= bit; break; case STORE_FAST: @@ -8194,6 +8223,7 @@ fast_scan_many_locals(basicblock *entryblock, int nlocals) assert(arg >= 0); switch (instr->i_opcode) { case DELETE_FAST: + case LOAD_FAST_AND_CLEAR: states[arg - 64] = blocknum - 1; break; case STORE_FAST: diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 59b3ef15d3ea0d..ba2f5a24754133 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -46,10 +46,11 @@ DISPATCH(); } - TARGET(LOAD_FAST_OR_NULL) { + TARGET(LOAD_FAST_AND_CLEAR) { PyObject *value; value = GETLOCAL(oparg); - Py_XINCREF(value); + // do not use SETLOCAL here, it decrefs the old value + GETLOCAL(oparg) = NULL; STACK_GROW(1); POKE(1, value); DISPATCH(); diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 71021e75b08097..b0db2d565cb062 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -16,7 +16,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 0; case LOAD_FAST: return 0; - case LOAD_FAST_OR_NULL: + case LOAD_FAST_AND_CLEAR: return 0; case LOAD_CONST: return 0; @@ -364,7 +364,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 1; case LOAD_FAST: return 1; - case LOAD_FAST_OR_NULL: + case LOAD_FAST_AND_CLEAR: return 1; case LOAD_CONST: return 1; @@ -711,7 +711,7 @@ struct opcode_metadata { [LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_FAST_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_FAST_OR_NULL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST_AND_CLEAR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index e04df563c0df91..4252228ee5a159 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -142,7 +142,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_BACKWARD, &&TARGET_COMPARE_AND_BRANCH, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_LOAD_FAST_OR_NULL, + &&TARGET_LOAD_FAST_AND_CLEAR, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD,