* Split YIELD_FROM opcode into SEND and JUMP_ABSOLUTE.
* Remove YIELD_FROM opcode.
static inline PyObject *_PyFrame_StackPeek(InterpreterFrame *f) {
assert(f->stacktop > f->f_code->co_nlocalsplus);
+ assert(f->localsplus[f->stacktop-1] != NULL);
return f->localsplus[f->stacktop-1];
}
#define GET_YIELD_FROM_ITER 69
#define PRINT_EXPR 70
#define LOAD_BUILD_CLASS 71
-#define YIELD_FROM 72
#define GET_AWAITABLE 73
#define LOAD_ASSERTION_ERROR 74
#define LIST_TO_TUPLE 82
#define COPY 120
#define JUMP_IF_NOT_EXC_MATCH 121
#define BINARY_OP 122
+#define SEND 123
#define LOAD_FAST 124
#define STORE_FAST 125
#define DELETE_FAST 126
#define LOAD_GLOBAL_BUILTIN 65
#define LOAD_METHOD_ADAPTIVE 66
#define LOAD_METHOD_CACHED 67
-#define LOAD_METHOD_CLASS 75
-#define LOAD_METHOD_MODULE 76
-#define LOAD_METHOD_NO_DICT 77
-#define STORE_ATTR_ADAPTIVE 78
-#define STORE_ATTR_INSTANCE_VALUE 79
-#define STORE_ATTR_SLOT 80
-#define STORE_ATTR_WITH_HINT 81
-#define LOAD_FAST__LOAD_FAST 87
-#define STORE_FAST__LOAD_FAST 123
+#define LOAD_METHOD_CLASS 72
+#define LOAD_METHOD_MODULE 75
+#define LOAD_METHOD_NO_DICT 76
+#define STORE_ATTR_ADAPTIVE 77
+#define STORE_ATTR_INSTANCE_VALUE 78
+#define STORE_ATTR_SLOT 79
+#define STORE_ATTR_WITH_HINT 80
+#define LOAD_FAST__LOAD_FAST 81
+#define STORE_FAST__LOAD_FAST 87
#define LOAD_FAST__LOAD_CONST 128
#define LOAD_CONST__LOAD_FAST 131
#define STORE_FAST__STORE_FAST 134
0U,
0U,
536870912U,
- 16384U,
+ 134234112U,
0U,
0U,
0U,
0U,
0U,
536870912U,
- 2182070272U,
+ 2316288000U,
0U,
0U,
0U,
# Python 3.11a3 3465 (Add COPY_FREE_VARS opcode)
# Python 3.11a3 3466 (bpo-45292: PEP-654 except*)
# Python 3.11a4 3467 (Change CALL_xxx opcodes)
+# Python 3.11a4 3468 (Add SEND opcode)
#
# MAGIC must change whenever the bytecode emitted by the compiler may no
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3467).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3468).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__'
def_op('GET_YIELD_FROM_ITER', 69)
def_op('PRINT_EXPR', 70)
def_op('LOAD_BUILD_CLASS', 71)
-def_op('YIELD_FROM', 72)
+
def_op('GET_AWAITABLE', 73)
def_op('LOAD_ASSERTION_ERROR', 74)
def_op('COPY', 120)
jabs_op('JUMP_IF_NOT_EXC_MATCH', 121)
def_op('BINARY_OP', 122)
-
+jrel_op('SEND', 123) # Number of bytes to skip
def_op('LOAD_FAST', 124) # Local variable number
haslocal.append(124)
def_op('STORE_FAST', 125) # Local variable number
--- /dev/null
+Remove the ``YIELD_FROM`` instruction and replace it with the ``SEND``
+instruction which performs the same operation, but without the loop.
next_stack = pop_value(pop_value(pop_value(next_stack)));
stacks[i+1] = next_stack;
break;
-
+ case SEND:
+ j = get_arg(code, i) + i + 1;
+ assert(j < len);
+ assert(stacks[j] == UNINITIALIZED || stacks[j] == pop_value(next_stack));
+ stacks[j] = pop_value(next_stack);
+ stacks[i+1] = next_stack;
+ break;
case JUMP_FORWARD:
j = get_arg(code, i) + i + 1;
assert(j < len);
#include "pycore_frame.h" // InterpreterFrame
#include "frameobject.h" // PyFrameObject
#include "structmember.h" // PyMemberDef
-#include "opcode.h" // YIELD_FROM
+#include "opcode.h" // SEND
static PyObject *gen_close(PyGenObject *, PyObject *);
static PyObject *async_gen_asend_new(PyAsyncGenObject *, PyObject *);
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
if (frame->f_lasti < 0) {
- /* Return immediately if the frame didn't start yet. YIELD_FROM
+ /* Return immediately if the frame didn't start yet. SEND
always come after LOAD_CONST: a code object should not start
- with YIELD_FROM */
- assert(code[0] != YIELD_FROM);
+ with SEND */
+ assert(code[0] != SEND);
return NULL;
}
- if (code[(frame->f_lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM)
+ if (code[frame->f_lasti*sizeof(_Py_CODEUNIT)] != SEND || frame->stacktop < 0)
return NULL;
yf = _PyFrame_StackPeek(frame);
Py_INCREF(yf);
ret = _PyFrame_StackPop((InterpreterFrame *)gen->gi_iframe);
assert(ret == yf);
Py_DECREF(ret);
- /* Termination repetition of YIELD_FROM */
+ /* Termination repetition of SEND loop */
assert(frame->f_lasti >= 0);
- frame->f_lasti += 1;
+ PyObject *bytecode = gen->gi_code->co_code;
+ unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
+ assert(code[frame->f_lasti*sizeof(_Py_CODEUNIT)] == SEND);
+ int jump = code[frame->f_lasti*sizeof(_Py_CODEUNIT)+1];
+ frame->f_lasti += jump;
if (_PyGen_FetchStopIterationValue(&val) == 0) {
ret = gen_send(gen, val);
Py_DECREF(val);
if (_Py_atomic_load_relaxed(eval_breaker)) {
opcode = _Py_OPCODE(*next_instr);
if (opcode != BEFORE_ASYNC_WITH &&
- opcode != YIELD_FROM) {
+ opcode != SEND &&
+ _Py_OPCODE(next_instr[-1]) != SEND) {
/* Few cases where we skip running signal handlers and other
pending calls:
- If we're about to enter the 'with:'. It will prevent
DISPATCH();
}
- TARGET(YIELD_FROM) {
+ TARGET(SEND) {
assert(frame->depth == 0);
+ assert(STACK_LEVEL() >= 2);
PyObject *v = POP();
PyObject *receiver = TOP();
PySendResult gen_status;
}
if (gen_status == PYGEN_RETURN) {
assert (retval != NULL);
-
Py_DECREF(receiver);
SET_TOP(retval);
- retval = NULL;
+ JUMPBY(oparg);
DISPATCH();
}
assert (gen_status == PYGEN_NEXT);
- /* receiver remains on stack, retval is value to be yielded */
- /* and repeat... */
- assert(frame->f_lasti > 0);
- frame->f_lasti -= 1;
+ assert (retval != NULL);
frame->f_state = FRAME_SUSPENDED;
_PyFrame_SetStackPointer(frame, stack_pointer);
TRACE_FUNCTION_EXIT();
return -1;
}
if (line != -1 && f->f_trace_lines) {
- /* Trace backward edges or if line number has changed */
- if (frame->f_lasti < instr_prev || line != lastline) {
+ /* Trace backward edges (except in 'yield from') or if line number has changed */
+ int trace = line != lastline ||
+ (frame->f_lasti < instr_prev &&
+ _Py_OPCODE(frame->f_code->co_firstinstr[frame->f_lasti]) != SEND);
+ if (trace) {
result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
}
}
return 0;
case YIELD_VALUE:
return 0;
- case YIELD_FROM:
- return -1;
case POP_BLOCK:
return 0;
case POP_EXCEPT:
case FOR_ITER:
/* -1 at end of iterator, 1 if continue iterating. */
return jump > 0 ? -1 : 1;
-
+ case SEND:
+ return jump > 0 ? -1 : 0;
case STORE_ATTR:
return -2;
case DELETE_ATTR:
the ASDL name to synthesize the name of the C type and the visit function.
*/
+#define ADD_YIELD_FROM(C) \
+ RETURN_IF_FALSE(compiler_add_yield_from((C)))
+
#define VISIT(C, TYPE, V) {\
if (!compiler_visit_ ## TYPE((C), (V))) \
return 0; \
return 1;
}
+static int
+compiler_add_yield_from(struct compiler *c)
+{
+ basicblock *start, *jump, *exit;
+ start = compiler_new_block(c);
+ jump = compiler_new_block(c);
+ exit = compiler_new_block(c);
+ if (start == NULL || jump == NULL || exit == NULL) {
+ return 0;
+ }
+ compiler_use_next_block(c, start);
+ ADDOP_JUMP(c, SEND, exit);
+ compiler_use_next_block(c, jump);
+ ADDOP_JUMP(c, JUMP_ABSOLUTE, start);
+ compiler_use_next_block(c, exit);
+ return 1;
+}
+
/* Unwind a frame block. If preserve_tos is true, the TOS before
* popping the blocks will be restored afterwards, unless another
* return, break or continue is found. In which case, the TOS will
if (info->fb_type == ASYNC_WITH) {
ADDOP(c, GET_AWAITABLE);
ADDOP_LOAD_CONST(c, Py_None);
- ADDOP(c, YIELD_FROM);
+ ADD_YIELD_FROM(c);
}
ADDOP(c, POP_TOP);
/* The exit block should appear to execute after the
ADDOP_JUMP(c, SETUP_FINALLY, except);
ADDOP(c, GET_ANEXT);
ADDOP_LOAD_CONST(c, Py_None);
- ADDOP(c, YIELD_FROM);
+ ADD_YIELD_FROM(c);
ADDOP(c, POP_BLOCK); /* for SETUP_FINALLY */
/* Success block for __anext__ */
ADDOP_JUMP(c, SETUP_FINALLY, except);
ADDOP(c, GET_ANEXT);
ADDOP_LOAD_CONST(c, Py_None);
- ADDOP(c, YIELD_FROM);
+ ADD_YIELD_FROM(c);
ADDOP(c, POP_BLOCK);
VISIT(c, expr, gen->target);
if (is_async_generator && type != COMP_GENEXP) {
ADDOP(c, GET_AWAITABLE);
ADDOP_LOAD_CONST(c, Py_None);
- ADDOP(c, YIELD_FROM);
+ ADD_YIELD_FROM(c);
}
return 1;
ADDOP(c, BEFORE_ASYNC_WITH);
ADDOP(c, GET_AWAITABLE);
ADDOP_LOAD_CONST(c, Py_None);
- ADDOP(c, YIELD_FROM);
+ ADD_YIELD_FROM(c);
ADDOP_JUMP(c, SETUP_WITH, final);
return 0;
ADDOP(c, GET_AWAITABLE);
ADDOP_LOAD_CONST(c, Py_None);
- ADDOP(c, YIELD_FROM);
+ ADD_YIELD_FROM(c);
ADDOP(c, POP_TOP);
ADDOP(c, WITH_EXCEPT_START);
ADDOP(c, GET_AWAITABLE);
ADDOP_LOAD_CONST(c, Py_None);
- ADDOP(c, YIELD_FROM);
+ ADD_YIELD_FROM(c);
compiler_with_except_finish(c, cleanup);
compiler_use_next_block(c, exit);
VISIT(c, expr, e->v.YieldFrom.value);
ADDOP(c, GET_YIELD_FROM_ITER);
ADDOP_LOAD_CONST(c, Py_None);
- ADDOP(c, YIELD_FROM);
+ ADD_YIELD_FROM(c);
break;
case Await_kind:
if (!IS_TOP_LEVEL_AWAIT(c)){
VISIT(c, expr, e->v.Await.value);
ADDOP(c, GET_AWAITABLE);
ADDOP_LOAD_CONST(c, Py_None);
- ADDOP(c, YIELD_FROM);
+ ADD_YIELD_FROM(c);
break;
case Compare_kind:
return compiler_compare(c, e);
continue;
}
struct instr *last = &b->b_instr[b->b_iused-1];
- if (last->i_opcode == JUMP_ABSOLUTE &&
- last->i_target->b_visited == 0
- ) {
- last->i_opcode = JUMP_FORWARD;
+ if (last->i_opcode == JUMP_ABSOLUTE) {
+ if (last->i_target->b_visited == 0) {
+ last->i_opcode = JUMP_FORWARD;
+ }
+ else if (b->b_iused >= 2 && b->b_instr[b->b_iused-2].i_opcode == SEND) {
+ last->i_opcode = JUMP_ABSOLUTE_QUICK;
+ }
}
}
}
&&TARGET_GET_YIELD_FROM_ITER,
&&TARGET_PRINT_EXPR,
&&TARGET_LOAD_BUILD_CLASS,
- &&TARGET_YIELD_FROM,
+ &&TARGET_LOAD_METHOD_CLASS,
&&TARGET_GET_AWAITABLE,
&&TARGET_LOAD_ASSERTION_ERROR,
- &&TARGET_LOAD_METHOD_CLASS,
&&TARGET_LOAD_METHOD_MODULE,
&&TARGET_LOAD_METHOD_NO_DICT,
&&TARGET_STORE_ATTR_ADAPTIVE,
&&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_STORE_ATTR_SLOT,
&&TARGET_STORE_ATTR_WITH_HINT,
+ &&TARGET_LOAD_FAST__LOAD_FAST,
&&TARGET_LIST_TO_TUPLE,
&&TARGET_RETURN_VALUE,
&&TARGET_IMPORT_STAR,
&&TARGET_SETUP_ANNOTATIONS,
&&TARGET_YIELD_VALUE,
- &&TARGET_LOAD_FAST__LOAD_FAST,
+ &&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_PREP_RERAISE_STAR,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
&&TARGET_COPY,
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
&&TARGET_BINARY_OP,
- &&TARGET_STORE_FAST__LOAD_FAST,
+ &&TARGET_SEND,
&&TARGET_LOAD_FAST,
&&TARGET_STORE_FAST,
&&TARGET_DELETE_FAST,