From e4a68550426dbea79bd9fc6ff0a75395891d35b7 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 20 Jan 2023 16:45:11 -0800 Subject: [PATCH 1/2] add opcode for more efficient comprehension execution --- Doc/library/dis.rst | 14 +++ Include/internal/pycore_frame.h | 9 +- Include/internal/pycore_opcode.h | 4 +- Include/opcode.h | 1 + Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 1 + Lib/test/test_dis.py | 6 +- Lib/test/test_sys.py | 4 +- Objects/frame_layout.md | 1 + Objects/frameobject.c | 5 +- Python/bytecodes.c | 23 ++++- Python/ceval.c | 22 ++--- Python/compile.c | 123 ++++++++++++++++----------- Python/frame.c | 2 + Python/generated_cases.c.h | 22 ++++- Python/opcode_metadata.h | 1 + Python/opcode_targets.h | 2 +- 17 files changed, 165 insertions(+), 78 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 6a68ec4b14be31..0313179cb27841 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -1304,6 +1304,20 @@ iterations of the loop. * 2: ``raise STACK[-2] from STACK[-1]`` (raise exception instance or type at ``STACK[-2]`` with ``__cause__`` set to ``STACK[-1]``) +.. opcode:: COMPREHENSION (flag) + + Calls a comprehension code object, without creating and throwing away a + single-use function object. ``flag`` must be either ``0`` or ``1``, the + latter indicating the comprehension has free variables and a closure tuple + will be on the stack. + + The stack should contain, from bottom to top: + + * a tuple containing cells for free variables, if ``flag`` is set + * the code object for the comprehension + * the single "argument" to the comprehension (the iterated object) + + .. versionadded:: 3.12 .. opcode:: CALL (argc) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index f12b225ebfccf2..057b026126af6a 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -52,6 +52,9 @@ typedef struct _PyInterpreterFrame { PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */ PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */ PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */ + // For comprehensions, f_closure and f_code may not match func_closure and + // func_code from f_funcobj above; f_funcobj will be the calling function. + PyObject *f_closure; /* Strong reference, may be NULL. Only valid if not on C stack */ PyCodeObject *f_code; /* Strong reference */ PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */ /* Linkage section */ @@ -112,12 +115,14 @@ void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest); static inline void _PyFrame_Initialize( _PyInterpreterFrame *frame, PyFunctionObject *func, - PyObject *locals, PyCodeObject *code, int null_locals_from) + PyObject *locals, PyCodeObject *code, PyObject *closure, + int null_locals_from) { frame->f_funcobj = (PyObject *)func; frame->f_code = (PyCodeObject *)Py_NewRef(code); frame->f_builtins = func->func_builtins; frame->f_globals = func->func_globals; + frame->f_closure = Py_XNewRef(closure); frame->f_locals = locals; frame->stacktop = code->co_nlocalsplus; frame->frame_obj = NULL; @@ -250,7 +255,7 @@ _PyFrame_PushUnchecked(PyThreadState *tstate, PyFunctionObject *func, int null_l _PyInterpreterFrame *new_frame = (_PyInterpreterFrame *)tstate->datastack_top; tstate->datastack_top += code->co_framesize; assert(tstate->datastack_top < tstate->datastack_limit); - _PyFrame_Initialize(new_frame, func, NULL, code, null_locals_from); + _PyFrame_Initialize(new_frame, func, NULL, code, func->func_closure, null_locals_from); return new_frame; } diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 05c0485b0641d8..83d2f27fabd05c 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -108,6 +108,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [COMPARE_AND_BRANCH_INT] = COMPARE_AND_BRANCH, [COMPARE_AND_BRANCH_STR] = COMPARE_AND_BRANCH, [COMPARE_OP] = COMPARE_OP, + [COMPREHENSION] = COMPREHENSION, [CONTAINS_OP] = CONTAINS_OP, [COPY] = COPY, [COPY_FREE_VARS] = COPY_FREE_VARS, @@ -398,7 +399,7 @@ static const char *const _PyOpcode_OpName[263] = { [167] = "<167>", [168] = "<168>", [169] = "<169>", - [170] = "<170>", + [COMPREHENSION] = "COMPREHENSION", [CALL] = "CALL", [KW_NAMES] = "KW_NAMES", [CALL_INTRINSIC_1] = "CALL_INTRINSIC_1", @@ -500,7 +501,6 @@ static const char *const _PyOpcode_OpName[263] = { case 167: \ case 168: \ case 169: \ - case 170: \ case 174: \ case 175: \ case 176: \ diff --git a/Include/opcode.h b/Include/opcode.h index 827f9931beb3e6..a22767b5d916f8 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -113,6 +113,7 @@ extern "C" { #define SET_UPDATE 163 #define DICT_MERGE 164 #define DICT_UPDATE 165 +#define COMPREHENSION 170 #define CALL 171 #define KW_NAMES 172 #define CALL_INTRINSIC_1 173 diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index cd44d0050ede82..cde871519bd6cf 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -431,6 +431,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a1 3515 (Embed jump mask in COMPARE_OP oparg) # Python 3.12a1 3516 (Add COMPARE_AND_BRANCH instruction) # Python 3.12a1 3517 (Change YIELD_VALUE oparg to exception block depth) +# Python 3.12a1 3518 (Add COMPREHENSION instruction) # Python 3.13 will start with 3550 @@ -443,7 +444,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 = (3517).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3518).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index c317e23beae62b..d5e0ac4d999333 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -218,6 +218,7 @@ def pseudo_op(name, op, real_ops): def_op('DICT_MERGE', 164) def_op('DICT_UPDATE', 165) +def_op('COMPREHENSION', 170) def_op('CALL', 171) def_op('KW_NAMES', 172) hasconst.append(172) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index bdf48c15309296..03bb4ceb595bb3 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -170,10 +170,9 @@ def bug1333982(x=[]): %3d LOAD_ASSERTION_ERROR LOAD_CONST 1 ( at 0x..., file "%s", line %d>) - MAKE_FUNCTION 0 LOAD_FAST 0 (x) GET_ITER - CALL 0 + COMPREHENSION 0 %3d LOAD_CONST 2 (1) @@ -675,10 +674,9 @@ def foo(x): %3d LOAD_CLOSURE 0 (x) BUILD_TUPLE 1 LOAD_CONST 1 ( at 0x..., file "%s", line %d>) - MAKE_FUNCTION 8 (closure) LOAD_DEREF 1 (y) GET_ITER - CALL 0 + COMPREHENSION 1 RETURN_VALUE """ % (dis_nested_0, __file__, diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ab1a0659471857..6894ce83092769 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1443,7 +1443,7 @@ class C(object): pass def func(): return sys._getframe() x = func() - check(x, size('3Pi3c7P2ic??2P')) + check(x, size('3Pi3c8P2ic??2P')) # function def func(): pass check(func, size('14Pi')) @@ -1460,7 +1460,7 @@ def bar(cls): check(bar, size('PP')) # generator def get_gen(): yield 1 - check(get_gen(), size('P2P4P4c7P2ic??2P')) + check(get_gen(), size('P2P4P4c8P2ic??2P')) # iterator check(iter('abc'), size('lP')) # callable-iterator diff --git a/Objects/frame_layout.md b/Objects/frame_layout.md index 2f95214db56498..bc13f83cab5cb2 100644 --- a/Objects/frame_layout.md +++ b/Objects/frame_layout.md @@ -75,6 +75,7 @@ The specials sections contains the following pointers: * Builtins dict * Locals dict (not the "fast" locals, but the locals for eval and class creation) * Code object +* Closure tuple of cells for free variables, if any. * Heap allocated `PyFrameObject` for this activation record, if any. * The function. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 6bc04bc8e848fc..cc84da974d68cd 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -882,6 +882,7 @@ frame_dealloc(PyFrameObject *f) frame->f_code = NULL; Py_CLEAR(frame->f_funcobj); Py_CLEAR(frame->f_locals); + Py_CLEAR(frame->f_closure); PyObject **locals = _PyFrame_GetLocalsArray(frame); for (int i = 0; i < frame->stacktop; i++) { Py_CLEAR(locals[i]); @@ -1020,7 +1021,7 @@ init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals) { PyCodeObject *code = (PyCodeObject *)func->func_code; _PyFrame_Initialize(frame, (PyFunctionObject*)Py_NewRef(func), - Py_XNewRef(locals), code, 0); + Py_XNewRef(locals), code, func->func_closure, 0); frame->previous = NULL; } @@ -1123,7 +1124,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame) } /* Free vars have not been initialized -- Do that */ - PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; + PyObject *closure = frame->f_closure; int offset = PyCode_GetFirstFree(co); for (int i = 0; i < co->co_nfreevars; ++i) { PyObject *o = PyTuple_GET_ITEM(closure, i); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ac54791d67e439..c871b3c3d390b7 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1265,8 +1265,7 @@ dummy_func( inst(COPY_FREE_VARS, (--)) { /* Copy closure variables to free variables */ PyCodeObject *co = frame->f_code; - assert(PyFunction_Check(frame->f_funcobj)); - PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; + PyObject *closure = frame->f_closure; assert(oparg == co->co_nfreevars); int offset = co->co_nlocalsplus - oparg; for (int i = 0; i < oparg; ++i) { @@ -2515,6 +2514,23 @@ dummy_func( kwnames = GETITEM(consts, oparg); } + // stack effect: (__0, __1, __array[oparg] --) + inst(COMPREHENSION) { + PyObject *code = PEEK(2); + PyObject *closure = oparg ? PEEK(3) : NULL; + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, Py_NewRef(frame->f_funcobj), code, closure, NULL, + stack_pointer - 1, 1, NULL + ); + Py_XDECREF(code); + Py_XDECREF(closure); + STACK_SHRINK(oparg + 2); + if (new_frame == NULL) { + goto error; + } + DISPATCH_INLINED(new_frame); + } + // stack effect: (__0, __array[oparg] -- ) inst(CALL) { #if ENABLE_SPECIALIZATION @@ -2553,8 +2569,9 @@ dummy_func( int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags; PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(function)); STACK_SHRINK(total_args); + PyFunctionObject *func = (PyFunctionObject *)function; _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)function, locals, + tstate, func, func->func_code, func->func_closure, locals, stack_pointer, positional_args, kwnames ); kwnames = NULL; diff --git a/Python/ceval.c b/Python/ceval.c index 95eb99b453345b..65deee5be834b4 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -210,8 +210,8 @@ static void format_awaitable_error(PyThreadState *, PyTypeObject *, int); static int get_exception_handler(PyCodeObject *, int, int*, int*, int*); static _PyInterpreterFrame * _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, - PyObject *locals, PyObject* const* args, - size_t argcount, PyObject *kwnames); + PyObject *code, PyObject *closure, PyObject *locals, + PyObject* const* args, size_t argcount, PyObject *kwnames); static void _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame); @@ -752,6 +752,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int entry_frame.frame_obj = (PyFrameObject*)0xaaa2; entry_frame.f_globals = (PyObject*)0xaaa3; entry_frame.f_builtins = (PyObject*)0xaaa4; + entry_frame.f_closure = (PyObject*)0xaaa5; #endif entry_frame.f_code = tstate->interp->interpreter_trampoline; entry_frame.prev_instr = @@ -1388,10 +1389,9 @@ get_exception_handler(PyCodeObject *code, int index, int *level, int *handler, i static int initialize_locals(PyThreadState *tstate, PyFunctionObject *func, - PyObject **localsplus, PyObject *const *args, + PyCodeObject *co, PyObject **localsplus, PyObject *const *args, Py_ssize_t argcount, PyObject *kwnames) { - PyCodeObject *co = (PyCodeObject*)func->func_code; const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; /* Create a dictionary for keyword parameters (**kwags) */ @@ -1613,18 +1613,19 @@ initialize_locals(PyThreadState *tstate, PyFunctionObject *func, /* Consumes references to func, locals and all the args */ static _PyInterpreterFrame * _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, - PyObject *locals, PyObject* const* args, - size_t argcount, PyObject *kwnames) + PyObject *codeobj, PyObject *closure, PyObject *locals, + PyObject* const* args, size_t argcount, PyObject *kwnames) { - PyCodeObject * code = (PyCodeObject *)func->func_code; CALL_STAT_INC(frames_pushed); + assert(PyCode_Check(codeobj)); + PyCodeObject *code = (PyCodeObject *)codeobj; _PyInterpreterFrame *frame = _PyThreadState_PushFrame(tstate, code->co_framesize); if (frame == NULL) { goto fail; } - _PyFrame_Initialize(frame, func, locals, code, 0); + _PyFrame_Initialize(frame, func, locals, code, closure, 0); PyObject **localsarray = &frame->localsplus[0]; - if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) { + if (initialize_locals(tstate, func, code, localsarray, args, argcount, kwnames)) { assert(frame->owner != FRAME_OWNED_BY_GENERATOR); _PyEvalFrameClearAndPop(tstate, frame); return NULL; @@ -1708,7 +1709,8 @@ _PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func, } } _PyInterpreterFrame *frame = _PyEvalFramePushAndInit( - tstate, func, locals, args, argcount, kwnames); + tstate, func, func->func_code, func->func_closure, locals, + args, argcount, kwnames); if (frame == NULL) { return NULL; } diff --git a/Python/compile.c b/Python/compile.c index 9fc997cdf525e9..05071ff3ff267e 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1243,6 +1243,8 @@ stack_effect(int opcode, int oparg, int jump) /* Functions and calls */ case KW_NAMES: return 0; + case COMPREHENSION: + return -1-oparg; case CALL: return -1-oparg; case CALL_INTRINSIC_1: @@ -2249,53 +2251,63 @@ compiler_lookup_arg(PyObject *dict, PyObject *name) static int compiler_make_closure(struct compiler *c, location loc, - PyCodeObject *co, Py_ssize_t flags) + PyCodeObject *co) { - if (co->co_nfreevars) { - int i = PyCode_GetFirstFree(co); - for (; i < co->co_nlocalsplus; ++i) { - /* Bypass com_addop_varname because it will generate - LOAD_DEREF but LOAD_CLOSURE is needed. - */ - PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); + int i = PyCode_GetFirstFree(co); + for (; i < co->co_nlocalsplus; ++i) { + /* Bypass com_addop_varname because it will generate + LOAD_DEREF but LOAD_CLOSURE is needed. + */ + PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); - /* Special case: If a class contains a method with a - free variable that has the same name as a method, - the name will be considered free *and* local in the - class. It should be handled by the closure, as - well as by the normal name lookup logic. - */ - int reftype = get_ref_type(c, name); - if (reftype == -1) { - return ERROR; - } - int arg; - if (reftype == CELL) { - arg = compiler_lookup_arg(c->u->u_cellvars, name); - } - else { - arg = compiler_lookup_arg(c->u->u_freevars, name); - } - if (arg == -1) { - PyObject *freevars = _PyCode_GetFreevars(co); - if (freevars == NULL) { - PyErr_Clear(); - } - PyErr_Format(PyExc_SystemError, - "compiler_lookup_arg(name=%R) with reftype=%d failed in %S; " - "freevars of code %S: %R", - name, - reftype, - c->u->u_name, - co->co_name, - freevars); - Py_DECREF(freevars); - return ERROR; + /* Special case: If a class contains a method with a + free variable that has the same name as a method, + the name will be considered free *and* local in the + class. It should be handled by the closure, as + well as by the normal name lookup logic. + */ + int reftype = get_ref_type(c, name); + if (reftype == -1) { + return ERROR; + } + int arg; + if (reftype == CELL) { + arg = compiler_lookup_arg(c->u->u_cellvars, name); + } + else { + arg = compiler_lookup_arg(c->u->u_freevars, name); + } + if (arg == -1) { + PyObject *freevars = _PyCode_GetFreevars(co); + if (freevars == NULL) { + PyErr_Clear(); } - ADDOP_I(c, loc, LOAD_CLOSURE, arg); + PyErr_Format(PyExc_SystemError, + "compiler_lookup_arg(name=%R) with reftype=%d failed in %S; " + "freevars of code %S: %R", + name, + reftype, + c->u->u_name, + co->co_name, + freevars); + Py_DECREF(freevars); + return ERROR; + } + ADDOP_I(c, loc, LOAD_CLOSURE, arg); + } + ADDOP_I(c, loc, BUILD_TUPLE, co->co_nfreevars); + return SUCCESS; +} + +static int +compiler_make_function(struct compiler *c, location loc, + PyCodeObject *co, Py_ssize_t flags) +{ + if (co->co_nfreevars) { + if(compiler_make_closure(c, loc, co) < 0) { + return ERROR; } flags |= 0x08; - ADDOP_I(c, loc, BUILD_TUPLE, co->co_nfreevars); } ADDOP_LOAD_CONST(c, loc, (PyObject*)co); ADDOP_I(c, loc, MAKE_FUNCTION, flags); @@ -2687,7 +2699,7 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async) Py_XDECREF(co); return ERROR; } - if (compiler_make_closure(c, loc, co, funcflags) < 0) { + if (compiler_make_function(c, loc, co, funcflags) < 0) { Py_DECREF(co); return ERROR; } @@ -2790,7 +2802,7 @@ compiler_class(struct compiler *c, stmt_ty s) ADDOP(c, loc, LOAD_BUILD_CLASS); /* 3. load a function (or closure) made from the code object */ - if (compiler_make_closure(c, loc, co, 0) < 0) { + if (compiler_make_function(c, loc, co, 0) < 0) { Py_DECREF(co); return ERROR; } @@ -3051,7 +3063,7 @@ compiler_lambda(struct compiler *c, expr_ty e) return ERROR; } - if (compiler_make_closure(c, loc, co, funcflags) < 0) { + if (compiler_make_function(c, loc, co, funcflags) < 0) { Py_DECREF(co); return ERROR; } @@ -5449,9 +5461,24 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, } loc = LOC(e); - if (compiler_make_closure(c, loc, co, 0) < 0) { - goto error; + int oparg = 0; + int opcode = COMPREHENSION; + + if (type == COMP_GENEXP || is_async_generator) { + opcode = CALL; + if (compiler_make_function(c, loc, co, 0) < 0) { + goto error; + } + } else { + if (co->co_nfreevars) { + oparg = 1; + if (compiler_make_closure(c, loc, co) < 0) { + goto error; + } + } + ADDOP_LOAD_CONST(c, loc, (PyObject*)co); } + Py_DECREF(co); VISIT(c, expr, outermost->iter); @@ -5463,7 +5490,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, ADDOP(c, loc, GET_ITER); } - ADDOP_I(c, loc, CALL, 0); + ADDOP_I(c, loc, opcode, oparg); if (is_async_generator && type != COMP_GENEXP) { ADDOP_I(c, loc, GET_AWAITABLE, 0); diff --git a/Python/frame.c b/Python/frame.c index 6a287d4724051a..93feb38ce8cf24 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -15,6 +15,7 @@ _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg) Py_VISIT(frame->f_locals); Py_VISIT(frame->f_funcobj); Py_VISIT(frame->f_code); + Py_VISIT(frame->f_closure); /* locals */ PyObject **locals = _PyFrame_GetLocalsArray(frame); int i = 0; @@ -145,6 +146,7 @@ _PyFrame_Clear(_PyInterpreterFrame *frame) Py_XDECREF(frame->f_locals); Py_DECREF(frame->f_funcobj); Py_DECREF(frame->f_code); + Py_XDECREF(frame->f_closure); } int diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 5dcc8eeec19f89..d63eafaea4c0f3 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1512,8 +1512,7 @@ TARGET(COPY_FREE_VARS) { /* Copy closure variables to free variables */ PyCodeObject *co = frame->f_code; - assert(PyFunction_Check(frame->f_funcobj)); - PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; + PyObject *closure = frame->f_closure; assert(oparg == co->co_nfreevars); int offset = co->co_nlocalsplus - oparg; for (int i = 0; i < oparg; ++i) { @@ -2907,6 +2906,22 @@ DISPATCH(); } + TARGET(COMPREHENSION) { + PyObject *code = PEEK(2); + PyObject *closure = oparg ? PEEK(3) : NULL; + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, Py_NewRef(frame->f_funcobj), code, closure, NULL, + stack_pointer - 1, 1, NULL + ); + Py_XDECREF(code); + Py_XDECREF(closure); + STACK_SHRINK(oparg + 2); + if (new_frame == NULL) { + goto error; + } + DISPATCH_INLINED(new_frame); + } + TARGET(CALL) { PREDICTED(CALL); #if ENABLE_SPECIALIZATION @@ -2945,8 +2960,9 @@ int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags; PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(function)); STACK_SHRINK(total_args); + PyFunctionObject *func = (PyFunctionObject *)function; _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)function, locals, + tstate, func, func->func_code, func->func_closure, locals, stack_pointer, positional_args, kwnames ); kwnames = NULL; diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 1fb0acceb511c7..60bd7ba2cd6352 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -153,6 +153,7 @@ static const struct { [LOAD_ATTR_METHOD_LAZY_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [CALL_BOUND_METHOD_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [KW_NAMES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [COMPREHENSION] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CALL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CALL_PY_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CALL_PY_WITH_DEFAULTS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index f1c3f3e0c4ee17..e92d2e7c4236ab 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -169,7 +169,7 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, + &&TARGET_COMPREHENSION, &&TARGET_CALL, &&TARGET_KW_NAMES, &&TARGET_CALL_INTRINSIC_1, From ee2ad56db82a9f0e5ba81d8ee2c7d97a21b4313d Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 25 Jan 2023 00:55:36 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-01-25-00-55-33.gh-issue-97933.1ET5Ig.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-25-00-55-33.gh-issue-97933.1ET5Ig.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-25-00-55-33.gh-issue-97933.1ET5Ig.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-25-00-55-33.gh-issue-97933.1ET5Ig.rst new file mode 100644 index 00000000000000..9f40e7483a4375 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-25-00-55-33.gh-issue-97933.1ET5Ig.rst @@ -0,0 +1 @@ +New COMPREHENSION bytecode instruction executes a comprehension more efficiently, without allocating a single-use function object.