Skip to content

Commit 0367a36

Browse files
authored
bpo-43683: Streamline YIELD_VALUE and SEND (GH-30723)
* Split YIELD_VALUE into ASYNC_GEN_WRAP; YIELD_VALUE for async generators. * Split SEND into SEND; YIELD_VALUE. * Document new opcodes.
1 parent d75a51b commit 0367a36

File tree

9 files changed

+72
-43
lines changed

9 files changed

+72
-43
lines changed

Doc/library/dis.rst

+16
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,22 @@ All of the following opcodes use their arguments.
12331233
.. versionadded:: 3.11
12341234

12351235

1236+
.. opcode:: SEND
1237+
1238+
Sends ``None`` to the sub-generator of this generator.
1239+
Used in ``yield from`` and ``await`` statements.
1240+
1241+
.. versionadded:: 3.11
1242+
1243+
1244+
.. opcode:: ASYNC_GEN_WRAP
1245+
1246+
Wraps the value on top of the stack in an ``async_generator_wrapped_value``.
1247+
Used to yield in async generators.
1248+
1249+
.. versionadded:: 3.11
1250+
1251+
12361252
.. opcode:: HAVE_ARGUMENT
12371253

12381254
This is not really an opcode. It identifies the dividing line between

Include/opcode.h

+7-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ def _write_atomic(path, data, mode=0o666):
381381
# Python 3.11a4 3473 (Add POP_JUMP_IF_NOT_NONE/POP_JUMP_IF_NONE opcodes)
382382
# Python 3.11a4 3474 (Add RESUME opcode)
383383
# Python 3.11a5 3475 (Add RETURN_GENERATOR opcode)
384+
# Python 3.11a5 3476 (Add ASYNC_GEN_WRAP opcode)
384385

385386
# Python 3.12 will start with magic number 3500
386387

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

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

400401
_PYCACHE = '__pycache__'

Lib/opcode.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def jabs_op(name, op):
101101
def_op('IMPORT_STAR', 84)
102102
def_op('SETUP_ANNOTATIONS', 85)
103103
def_op('YIELD_VALUE', 86)
104-
104+
def_op('ASYNC_GEN_WRAP', 87)
105105
def_op('PREP_RERAISE_STAR', 88)
106106
def_op('POP_EXCEPT', 89)
107107

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add ASYNC_GEN_WRAP opcode to wrap the value to be yielded in async
2+
generators. Removes the need to special case async generators in the
3+
``YIELD_VALUE`` instruction.

Objects/genobject.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -353,15 +353,15 @@ _PyGen_yf(PyGenObject *gen)
353353
PyObject *bytecode = gen->gi_code->co_code;
354354
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
355355

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

364-
if (code[frame->f_lasti*sizeof(_Py_CODEUNIT)] != SEND || frame->stacktop < 0)
364+
if (code[(frame->f_lasti-1)*sizeof(_Py_CODEUNIT)] != SEND || frame->stacktop < 0)
365365
return NULL;
366366
yf = _PyFrame_StackPeek(frame);
367367
Py_INCREF(yf);
@@ -488,6 +488,8 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
488488
assert(frame->f_lasti >= 0);
489489
PyObject *bytecode = gen->gi_code->co_code;
490490
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
491+
/* Backup to SEND */
492+
frame->f_lasti--;
491493
assert(code[frame->f_lasti*sizeof(_Py_CODEUNIT)] == SEND);
492494
int jump = code[frame->f_lasti*sizeof(_Py_CODEUNIT)+1];
493495
frame->f_lasti += jump;

Python/ceval.c

+14-21
Original file line numberDiff line numberDiff line change
@@ -2650,32 +2650,25 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
26502650
}
26512651
assert (gen_status == PYGEN_NEXT);
26522652
assert (retval != NULL);
2653-
frame->f_state = FRAME_SUSPENDED;
2654-
_PyFrame_SetStackPointer(frame, stack_pointer);
2655-
TRACE_FUNCTION_EXIT();
2656-
DTRACE_FUNCTION_EXIT();
2657-
_Py_LeaveRecursiveCall(tstate);
2658-
/* Restore previous cframe and return. */
2659-
tstate->cframe = cframe.previous;
2660-
tstate->cframe->use_tracing = cframe.use_tracing;
2661-
assert(tstate->cframe->current_frame == frame->previous);
2662-
assert(!_PyErr_Occurred(tstate));
2663-
return retval;
2653+
PUSH(retval);
2654+
DISPATCH();
2655+
}
2656+
2657+
TARGET(ASYNC_GEN_WRAP) {
2658+
PyObject *v = TOP();
2659+
assert(frame->f_code->co_flags & CO_ASYNC_GENERATOR);
2660+
PyObject *w = _PyAsyncGenValueWrapperNew(v);
2661+
if (w == NULL) {
2662+
goto error;
2663+
}
2664+
SET_TOP(w);
2665+
Py_DECREF(v);
2666+
DISPATCH();
26642667
}
26652668

26662669
TARGET(YIELD_VALUE) {
26672670
assert(frame->is_entry);
26682671
PyObject *retval = POP();
2669-
2670-
if (frame->f_code->co_flags & CO_ASYNC_GENERATOR) {
2671-
PyObject *w = _PyAsyncGenValueWrapperNew(retval);
2672-
Py_DECREF(retval);
2673-
if (w == NULL) {
2674-
retval = NULL;
2675-
goto error;
2676-
}
2677-
retval = w;
2678-
}
26792672
frame->f_state = FRAME_SUSPENDED;
26802673
_PyFrame_SetStackPointer(frame, stack_pointer);
26812674
TRACE_FUNCTION_EXIT();

Python/compile.c

+19-6
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,7 @@ stack_effect(int opcode, int oparg, int jump)
910910
return -1;
911911
case SETUP_ANNOTATIONS:
912912
return 0;
913+
case ASYNC_GEN_WRAP:
913914
case YIELD_VALUE:
914915
return 0;
915916
case POP_BLOCK:
@@ -1541,6 +1542,9 @@ compiler_addop_j_noline(struct compiler *c, int opcode, basicblock *b)
15411542
#define POP_EXCEPT_AND_RERAISE(C) \
15421543
RETURN_IF_FALSE(compiler_pop_except_and_reraise((C)))
15431544

1545+
#define ADDOP_YIELD(C) \
1546+
RETURN_IF_FALSE(addop_yield(C))
1547+
15441548
#define VISIT(C, TYPE, V) {\
15451549
if (!compiler_visit_ ## TYPE((C), (V))) \
15461550
return 0; \
@@ -1844,6 +1848,7 @@ compiler_add_yield_from(struct compiler *c, int await)
18441848
compiler_use_next_block(c, start);
18451849
ADDOP_JUMP(c, SEND, exit);
18461850
compiler_use_next_block(c, resume);
1851+
ADDOP(c, YIELD_VALUE);
18471852
ADDOP_I(c, RESUME, await ? 3 : 2);
18481853
ADDOP_JUMP(c, JUMP_NO_INTERRUPT, start);
18491854
compiler_use_next_block(c, exit);
@@ -4094,6 +4099,17 @@ addop_binary(struct compiler *c, operator_ty binop, bool inplace)
40944099
return 1;
40954100
}
40964101

4102+
4103+
static int
4104+
addop_yield(struct compiler *c) {
4105+
if (c->u->u_ste->ste_generator && c->u->u_ste->ste_coroutine) {
4106+
ADDOP(c, ASYNC_GEN_WRAP);
4107+
}
4108+
ADDOP(c, YIELD_VALUE);
4109+
ADDOP_I(c, RESUME, 1);
4110+
return 1;
4111+
}
4112+
40974113
static int
40984114
compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx)
40994115
{
@@ -5144,8 +5160,7 @@ compiler_sync_comprehension_generator(struct compiler *c,
51445160
switch (type) {
51455161
case COMP_GENEXP:
51465162
VISIT(c, expr, elt);
5147-
ADDOP(c, YIELD_VALUE);
5148-
ADDOP_I(c, RESUME, 1);
5163+
ADDOP_YIELD(c);
51495164
ADDOP(c, POP_TOP);
51505165
break;
51515166
case COMP_LISTCOMP:
@@ -5243,8 +5258,7 @@ compiler_async_comprehension_generator(struct compiler *c,
52435258
switch (type) {
52445259
case COMP_GENEXP:
52455260
VISIT(c, expr, elt);
5246-
ADDOP(c, YIELD_VALUE);
5247-
ADDOP_I(c, RESUME, 1);
5261+
ADDOP_YIELD(c);
52485262
ADDOP(c, POP_TOP);
52495263
break;
52505264
case COMP_LISTCOMP:
@@ -5714,8 +5728,7 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
57145728
else {
57155729
ADDOP_LOAD_CONST(c, Py_None);
57165730
}
5717-
ADDOP(c, YIELD_VALUE);
5718-
ADDOP_I(c, RESUME, 1);
5731+
ADDOP_YIELD(c);
57195732
break;
57205733
case YieldFrom_kind:
57215734
if (c->u->u_ste->ste_type != FunctionBlock)

Python/opcode_targets.h

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)