Skip to content

Commit

Permalink
bpo-43683: Streamline YIELD_VALUE and SEND (GH-30723)
Browse files Browse the repository at this point in the history
* Split YIELD_VALUE into ASYNC_GEN_WRAP; YIELD_VALUE for async generators.

* Split SEND into SEND; YIELD_VALUE.

* Document new opcodes.
  • Loading branch information
markshannon authored Jan 24, 2022
1 parent d75a51b commit 0367a36
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 43 deletions.
16 changes: 16 additions & 0 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,22 @@ All of the following opcodes use their arguments.
.. versionadded:: 3.11


.. opcode:: SEND

Sends ``None`` to the sub-generator of this generator.
Used in ``yield from`` and ``await`` statements.

.. versionadded:: 3.11


.. opcode:: ASYNC_GEN_WRAP

Wraps the value on top of the stack in an ``async_generator_wrapped_value``.
Used to yield in async generators.

.. versionadded:: 3.11


.. opcode:: HAVE_ARGUMENT

This is not really an opcode. It identifies the dividing line between
Expand Down
13 changes: 7 additions & 6 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.11a4 3473 (Add POP_JUMP_IF_NOT_NONE/POP_JUMP_IF_NONE opcodes)
# Python 3.11a4 3474 (Add RESUME opcode)
# Python 3.11a5 3475 (Add RETURN_GENERATOR opcode)
# Python 3.11a5 3476 (Add ASYNC_GEN_WRAP opcode)

# Python 3.12 will start with magic number 3500

Expand All @@ -394,7 +395,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3475).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3476).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

_PYCACHE = '__pycache__'
Expand Down
2 changes: 1 addition & 1 deletion Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def jabs_op(name, op):
def_op('IMPORT_STAR', 84)
def_op('SETUP_ANNOTATIONS', 85)
def_op('YIELD_VALUE', 86)

def_op('ASYNC_GEN_WRAP', 87)
def_op('PREP_RERAISE_STAR', 88)
def_op('POP_EXCEPT', 89)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add ASYNC_GEN_WRAP opcode to wrap the value to be yielded in async
generators. Removes the need to special case async generators in the
``YIELD_VALUE`` instruction.
6 changes: 4 additions & 2 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,15 +353,15 @@ _PyGen_yf(PyGenObject *gen)
PyObject *bytecode = gen->gi_code->co_code;
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);

if (frame->f_lasti < 0) {
if (frame->f_lasti < 1) {
/* Return immediately if the frame didn't start yet. SEND
always come after LOAD_CONST: a code object should not start
with SEND */
assert(code[0] != SEND);
return NULL;
}

if (code[frame->f_lasti*sizeof(_Py_CODEUNIT)] != SEND || frame->stacktop < 0)
if (code[(frame->f_lasti-1)*sizeof(_Py_CODEUNIT)] != SEND || frame->stacktop < 0)
return NULL;
yf = _PyFrame_StackPeek(frame);
Py_INCREF(yf);
Expand Down Expand Up @@ -488,6 +488,8 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
assert(frame->f_lasti >= 0);
PyObject *bytecode = gen->gi_code->co_code;
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
/* Backup to SEND */
frame->f_lasti--;
assert(code[frame->f_lasti*sizeof(_Py_CODEUNIT)] == SEND);
int jump = code[frame->f_lasti*sizeof(_Py_CODEUNIT)+1];
frame->f_lasti += jump;
Expand Down
35 changes: 14 additions & 21 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2650,32 +2650,25 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
}
assert (gen_status == PYGEN_NEXT);
assert (retval != NULL);
frame->f_state = FRAME_SUSPENDED;
_PyFrame_SetStackPointer(frame, stack_pointer);
TRACE_FUNCTION_EXIT();
DTRACE_FUNCTION_EXIT();
_Py_LeaveRecursiveCall(tstate);
/* Restore previous cframe and return. */
tstate->cframe = cframe.previous;
tstate->cframe->use_tracing = cframe.use_tracing;
assert(tstate->cframe->current_frame == frame->previous);
assert(!_PyErr_Occurred(tstate));
return retval;
PUSH(retval);
DISPATCH();
}

TARGET(ASYNC_GEN_WRAP) {
PyObject *v = TOP();
assert(frame->f_code->co_flags & CO_ASYNC_GENERATOR);
PyObject *w = _PyAsyncGenValueWrapperNew(v);
if (w == NULL) {
goto error;
}
SET_TOP(w);
Py_DECREF(v);
DISPATCH();
}

TARGET(YIELD_VALUE) {
assert(frame->is_entry);
PyObject *retval = POP();

if (frame->f_code->co_flags & CO_ASYNC_GENERATOR) {
PyObject *w = _PyAsyncGenValueWrapperNew(retval);
Py_DECREF(retval);
if (w == NULL) {
retval = NULL;
goto error;
}
retval = w;
}
frame->f_state = FRAME_SUSPENDED;
_PyFrame_SetStackPointer(frame, stack_pointer);
TRACE_FUNCTION_EXIT();
Expand Down
25 changes: 19 additions & 6 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,7 @@ stack_effect(int opcode, int oparg, int jump)
return -1;
case SETUP_ANNOTATIONS:
return 0;
case ASYNC_GEN_WRAP:
case YIELD_VALUE:
return 0;
case POP_BLOCK:
Expand Down Expand Up @@ -1541,6 +1542,9 @@ compiler_addop_j_noline(struct compiler *c, int opcode, basicblock *b)
#define POP_EXCEPT_AND_RERAISE(C) \
RETURN_IF_FALSE(compiler_pop_except_and_reraise((C)))

#define ADDOP_YIELD(C) \
RETURN_IF_FALSE(addop_yield(C))

#define VISIT(C, TYPE, V) {\
if (!compiler_visit_ ## TYPE((C), (V))) \
return 0; \
Expand Down Expand Up @@ -1844,6 +1848,7 @@ compiler_add_yield_from(struct compiler *c, int await)
compiler_use_next_block(c, start);
ADDOP_JUMP(c, SEND, exit);
compiler_use_next_block(c, resume);
ADDOP(c, YIELD_VALUE);
ADDOP_I(c, RESUME, await ? 3 : 2);
ADDOP_JUMP(c, JUMP_NO_INTERRUPT, start);
compiler_use_next_block(c, exit);
Expand Down Expand Up @@ -4094,6 +4099,17 @@ addop_binary(struct compiler *c, operator_ty binop, bool inplace)
return 1;
}


static int
addop_yield(struct compiler *c) {
if (c->u->u_ste->ste_generator && c->u->u_ste->ste_coroutine) {
ADDOP(c, ASYNC_GEN_WRAP);
}
ADDOP(c, YIELD_VALUE);
ADDOP_I(c, RESUME, 1);
return 1;
}

static int
compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx)
{
Expand Down Expand Up @@ -5144,8 +5160,7 @@ compiler_sync_comprehension_generator(struct compiler *c,
switch (type) {
case COMP_GENEXP:
VISIT(c, expr, elt);
ADDOP(c, YIELD_VALUE);
ADDOP_I(c, RESUME, 1);
ADDOP_YIELD(c);
ADDOP(c, POP_TOP);
break;
case COMP_LISTCOMP:
Expand Down Expand Up @@ -5243,8 +5258,7 @@ compiler_async_comprehension_generator(struct compiler *c,
switch (type) {
case COMP_GENEXP:
VISIT(c, expr, elt);
ADDOP(c, YIELD_VALUE);
ADDOP_I(c, RESUME, 1);
ADDOP_YIELD(c);
ADDOP(c, POP_TOP);
break;
case COMP_LISTCOMP:
Expand Down Expand Up @@ -5714,8 +5728,7 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
else {
ADDOP_LOAD_CONST(c, Py_None);
}
ADDOP(c, YIELD_VALUE);
ADDOP_I(c, RESUME, 1);
ADDOP_YIELD(c);
break;
case YieldFrom_kind:
if (c->u->u_ste->ste_type != FunctionBlock)
Expand Down
12 changes: 6 additions & 6 deletions Python/opcode_targets.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0367a36

Please sign in to comment.