From 135cabd328504e1648d17242b42b675cdbd0193b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 23 Nov 2021 09:53:24 +0000 Subject: [PATCH] bpo-44525: Copy free variables in bytecode to allow calls to inner functions to be specialized (GH-29595) * Make internal APIs that take PyFrameConstructor take a PyFunctionObject instead. * Add reference to function to frame, borrow references to builtins and globals. * Add COPY_FREE_VARS instruction to allow specialization of calls to inner functions. --- Doc/library/dis.rst | 9 ++ Include/cpython/funcobject.h | 3 - Include/internal/pycore_ceval.h | 4 +- Include/internal/pycore_frame.h | 28 +++-- Include/internal/pycore_function.h | 11 ++ Include/opcode.h | 1 + Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 1 + Lib/test/test_code.py | 4 +- Lib/test/test_dis.py | 106 +++++++++-------- Makefile.pre.in | 1 + .../2021-11-18-10-02-02.bpo-44525.M4xwn_.rst | 3 + Objects/call.c | 6 +- Objects/frameobject.c | 32 +++-- Objects/funcobject.c | 33 ++++++ Objects/genobject.c | 21 ++-- Objects/typeobject.c | 2 +- PCbuild/pythoncore.vcxproj | 1 + Python/bltinmodule.c | 3 +- Python/ceval.c | 112 +++++++++++------- Python/compile.c | 21 +++- Python/frame.c | 6 +- Python/opcode_targets.h | 2 +- Python/pystate.c | 6 +- Python/specialize.c | 5 +- 25 files changed, 268 insertions(+), 156 deletions(-) create mode 100644 Include/internal/pycore_function.h create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-11-18-10-02-02.bpo-44525.M4xwn_.rst diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 5fe9f65f46a..6fe64be9f0f 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -991,6 +991,15 @@ All of the following opcodes use their arguments. ``i`` is no longer offset by the length of ``co_varnames``. +.. opcode:: COPY_FREE_VARS (n) + + Copies the ``n`` free variables from the closure into the frame. + Removes the need for special code on the caller's side when calling + closures. + + .. versionadded:: 3.11 + + .. opcode:: RAISE_VARARGS (argc) Raises an exception using one of the 3 forms of the ``raise`` statement, diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 60b702218a1..9f0560fb725 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -101,9 +101,6 @@ uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func); #define PyFunction_GET_ANNOTATIONS(func) \ (((PyFunctionObject *)func) -> func_annotations) -#define PyFunction_AS_FRAME_CONSTRUCTOR(func) \ - ((PyFrameConstructor *)&((PyFunctionObject *)(func))->func_globals) - /* The classmethod and staticmethod types lives here, too */ PyAPI_DATA(PyTypeObject) PyClassMethod_Type; PyAPI_DATA(PyTypeObject) PyStaticMethod_Type; diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index c2251b04be6..9987f2076cd 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -52,7 +52,7 @@ _PyEval_EvalFrame(PyThreadState *tstate, struct _interpreter_frame *frame, int t extern PyObject * _PyEval_Vector(PyThreadState *tstate, - PyFrameConstructor *desc, PyObject *locals, + PyFunctionObject *func, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames); @@ -113,7 +113,7 @@ static inline void _Py_LeaveRecursiveCall_inline(void) { struct _interpreter_frame *_PyEval_GetFrame(void); -PyObject *_Py_MakeCoro(PyFrameConstructor *, struct _interpreter_frame *); +PyObject *_Py_MakeCoro(PyFunctionObject *func, struct _interpreter_frame *); #ifdef __cplusplus } diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index b025ca0b8d7..e36241f4a6a 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -20,13 +20,13 @@ enum _framestate { typedef signed char PyFrameState; typedef struct _interpreter_frame { - PyObject *f_globals; - PyObject *f_builtins; - PyObject *f_locals; - PyCodeObject *f_code; - PyFrameObject *frame_obj; - /* Borrowed reference to a generator, or NULL */ - PyObject *generator; + PyFunctionObject *f_func; /* Strong reference */ + PyObject *f_globals; /* Borrowed reference */ + PyObject *f_builtins; /* Borrowed reference */ + PyObject *f_locals; /* Strong reference, may be NULL */ + PyCodeObject *f_code; /* Strong reference */ + PyFrameObject *frame_obj; /* Strong reference, may be NULL */ + PyObject *generator; /* Borrowed reference, may be NULL */ struct _interpreter_frame *previous; int f_lasti; /* Last instruction if called */ int stacktop; /* Offset of TOS from localsplus */ @@ -70,16 +70,18 @@ static inline void _PyFrame_StackPush(InterpreterFrame *f, PyObject *value) { #define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *)) InterpreterFrame * -_PyInterpreterFrame_HeapAlloc(PyFrameConstructor *con, PyObject *locals); +_PyInterpreterFrame_HeapAlloc(PyFunctionObject *func, PyObject *locals); static inline void _PyFrame_InitializeSpecials( - InterpreterFrame *frame, PyFrameConstructor *con, + InterpreterFrame *frame, PyFunctionObject *func, PyObject *locals, int nlocalsplus) { - frame->f_code = (PyCodeObject *)Py_NewRef(con->fc_code); - frame->f_builtins = Py_NewRef(con->fc_builtins); - frame->f_globals = Py_NewRef(con->fc_globals); + Py_INCREF(func); + frame->f_func = func; + frame->f_code = (PyCodeObject *)Py_NewRef(func->func_code); + frame->f_builtins = func->func_builtins; + frame->f_globals = func->func_globals; frame->f_locals = Py_XNewRef(locals); frame->stacktop = nlocalsplus; frame->frame_obj = NULL; @@ -150,7 +152,7 @@ void _PyFrame_LocalsToFast(InterpreterFrame *frame, int clear); InterpreterFrame *_PyThreadState_PushFrame( - PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals); + PyThreadState *tstate, PyFunctionObject *func, PyObject *locals); extern InterpreterFrame * _PyThreadState_BumpFramePointerSlow(PyThreadState *tstate, size_t size); diff --git a/Include/internal/pycore_function.h b/Include/internal/pycore_function.h new file mode 100644 index 00000000000..dc4422df3eb --- /dev/null +++ b/Include/internal/pycore_function.h @@ -0,0 +1,11 @@ +#ifndef Py_INTERNAL_FUNCTION_H +#define Py_INTERNAL_FUNCTION_H + + +#include "Python.h" + +PyFunctionObject * +_PyFunction_FromConstructor(PyFrameConstructor *constr); + + +#endif /* !Py_INTERNAL_FUNCTION_H */ diff --git a/Include/opcode.h b/Include/opcode.h index 3ec89bd4c0b..2c1a212cbd6 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -100,6 +100,7 @@ extern "C" { #define SET_ADD 146 #define MAP_ADD 147 #define LOAD_CLASSDEREF 148 +#define COPY_FREE_VARS 149 #define MATCH_CLASS 152 #define FORMAT_VALUE 155 #define BUILD_CONST_KEY_MAP 156 diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 303ca728ec8..85c5193407d 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -370,6 +370,7 @@ def _write_atomic(path, data, mode=0o666): # active exception) # Python 3.11a3 3464 (bpo-45636: Merge numeric BINARY_*/INPLACE_* into # BINARY_OP) +# Python 3.11a3 3465 (Add COPY_FREE_VARS opcode) # # MAGIC must change whenever the bytecode emitted by the compiler may no @@ -379,7 +380,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 = (3464).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3465).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/opcode.py b/Lib/opcode.py index 3603bb422b1..60805e92ff3 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -177,6 +177,7 @@ def jabs_op(name, op): def_op('MAP_ADD', 147) def_op('LOAD_CLASSDEREF', 148) hasfree.append(148) +def_op('COPY_FREE_VARS', 149) def_op('MATCH_CLASS', 152) diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 0d46258d0c3..b42213bde07 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -141,6 +141,8 @@ check_impl_detail, requires_debug_ranges, gc_collect) from test.support.script_helper import assert_python_ok +from opcode import opmap +COPY_FREE_VARS = opmap['COPY_FREE_VARS'] def consts(t): @@ -185,7 +187,7 @@ def create_closure(__class__): def new_code(c): '''A new code object with a __class__ cell added to freevars''' - return c.replace(co_freevars=c.co_freevars + ('__class__',)) + return c.replace(co_freevars=c.co_freevars + ('__class__',), co_code=bytes([COPY_FREE_VARS, 1])+c.co_code) def add_foreign_method(cls, name, f): code = new_code(f.__code__) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 3b8ebb5dba9..dd328f072e4 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -459,16 +459,17 @@ def foo(x): dis_nested_1 = """%s Disassembly of : - 0 MAKE_CELL 0 (x) + 0 COPY_FREE_VARS 1 + 2 MAKE_CELL 0 (x) -%3d 2 LOAD_CLOSURE 0 (x) - 4 BUILD_TUPLE 1 - 6 LOAD_CONST 1 ( at 0x..., file "%s", line %d>) - 8 MAKE_FUNCTION 8 (closure) - 10 LOAD_DEREF 1 (y) - 12 GET_ITER - 14 CALL_FUNCTION 1 - 16 RETURN_VALUE +%3d 4 LOAD_CLOSURE 0 (x) + 6 BUILD_TUPLE 1 + 8 LOAD_CONST 1 ( at 0x..., file "%s", line %d>) + 10 MAKE_FUNCTION 8 (closure) + 12 LOAD_DEREF 1 (y) + 14 GET_ITER + 16 CALL_FUNCTION 1 + 18 RETURN_VALUE """ % (dis_nested_0, __file__, _h.__code__.co_firstlineno + 1, @@ -479,16 +480,18 @@ def foo(x): dis_nested_2 = """%s Disassembly of at 0x..., file "%s", line %d>: -%3d 0 BUILD_LIST 0 - 2 LOAD_FAST 0 (.0) - >> 4 FOR_ITER 6 (to 18) - 6 STORE_FAST 1 (z) - 8 LOAD_DEREF 2 (x) - 10 LOAD_FAST 1 (z) - 12 BINARY_OP 0 (+) - 14 LIST_APPEND 2 - 16 JUMP_ABSOLUTE 2 (to 4) - >> 18 RETURN_VALUE + 0 COPY_FREE_VARS 1 + +%3d 2 BUILD_LIST 0 + 4 LOAD_FAST 0 (.0) + >> 6 FOR_ITER 6 (to 20) + 8 STORE_FAST 1 (z) + 10 LOAD_DEREF 2 (x) + 12 LOAD_FAST 1 (z) + 14 BINARY_OP 0 (+) + 16 LIST_APPEND 2 + 18 JUMP_ABSOLUTE 3 (to 6) + >> 20 RETURN_VALUE """ % (dis_nested_1, __file__, _h.__code__.co_firstlineno + 3, @@ -1007,42 +1010,43 @@ def _prepare_test_cases(): Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=40, starts_line=None, is_jump_target=False, positions=None), ] - expected_opinfo_f = [ - Instruction(opname='MAKE_CELL', opcode=135, arg=0, argval='c', argrepr='c', offset=0, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='MAKE_CELL', opcode=135, arg=1, argval='d', argrepr='d', offset=2, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval=(5, 6), argrepr='(5, 6)', offset=4, starts_line=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CLOSURE', opcode=136, arg=3, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CLOSURE', opcode=136, arg=4, argval='b', argrepr='b', offset=8, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CLOSURE', opcode=136, arg=0, argval='c', argrepr='c', offset=10, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CLOSURE', opcode=136, arg=1, argval='d', argrepr='d', offset=12, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=14, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=16, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=18, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=20, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=22, starts_line=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='a', argrepr='a', offset=24, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='b', argrepr='b', offset=26, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=137, arg=0, argval='c', argrepr='c', offset=28, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=137, arg=1, argval='d', argrepr='d', offset=30, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='', offset=32, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=34, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=36, starts_line=6, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=38, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY_FREE_VARS', opcode=149, arg=2, argval=2, argrepr='', offset=0, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='MAKE_CELL', opcode=135, arg=0, argval='c', argrepr='c', offset=2, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='MAKE_CELL', opcode=135, arg=1, argval='d', argrepr='d', offset=4, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval=(5, 6), argrepr='(5, 6)', offset=6, starts_line=3, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CLOSURE', opcode=136, arg=3, argval='a', argrepr='a', offset=8, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CLOSURE', opcode=136, arg=4, argval='b', argrepr='b', offset=10, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CLOSURE', opcode=136, arg=0, argval='c', argrepr='c', offset=12, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CLOSURE', opcode=136, arg=1, argval='d', argrepr='d', offset=14, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=16, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=18, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=20, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=22, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=24, starts_line=5, is_jump_target=False, positions=None), + Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='a', argrepr='a', offset=26, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='b', argrepr='b', offset=28, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_DEREF', opcode=137, arg=0, argval='c', argrepr='c', offset=30, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_DEREF', opcode=137, arg=1, argval='d', argrepr='d', offset=32, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='', offset=34, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=36, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=38, starts_line=6, is_jump_target=False, positions=None), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=40, starts_line=None, is_jump_target=False, positions=None), ] expected_opinfo_inner = [ - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=0, starts_line=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=137, arg=2, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=137, arg=5, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='e', argrepr='e', offset=10, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=12, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=6, argval=6, argrepr='', offset=14, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=16, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=18, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=20, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY_FREE_VARS', opcode=149, arg=4, argval=4, argrepr='', offset=0, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=2, starts_line=4, is_jump_target=False, positions=None), + Instruction(opname='LOAD_DEREF', opcode=137, arg=2, argval='a', argrepr='a', offset=4, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='b', argrepr='b', offset=6, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='c', argrepr='c', offset=8, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_DEREF', opcode=137, arg=5, argval='d', argrepr='d', offset=10, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='e', argrepr='e', offset=12, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=14, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=6, argval=6, argrepr='', offset=16, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=18, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=20, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=22, starts_line=None, is_jump_target=False, positions=None), ] expected_opinfo_jumpy = [ diff --git a/Makefile.pre.in b/Makefile.pre.in index afa0414ce04..046b0e86e67 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1605,6 +1605,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_fileutils.h \ $(srcdir)/Include/internal/pycore_floatobject.h \ $(srcdir)/Include/internal/pycore_format.h \ + $(srcdir)/Include/internal/pycore_function.h \ $(srcdir)/Include/internal/pycore_getopt.h \ $(srcdir)/Include/internal/pycore_gil.h \ $(srcdir)/Include/internal/pycore_hamt.h \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-11-18-10-02-02.bpo-44525.M4xwn_.rst b/Misc/NEWS.d/next/Core and Builtins/2021-11-18-10-02-02.bpo-44525.M4xwn_.rst new file mode 100644 index 00000000000..f1c806d4858 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-11-18-10-02-02.bpo-44525.M4xwn_.rst @@ -0,0 +1,3 @@ +Adds new :opcode:`COPY_FREE_VARS` opcode, to make copying of free variables +from function to frame explicit. Helps optimization of calls to Python +function. diff --git a/Objects/call.c b/Objects/call.c index 5e55518b04c..310a2d732d1 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -383,16 +383,16 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack, size_t nargsf, PyObject *kwnames) { assert(PyFunction_Check(func)); - PyFrameConstructor *f = PyFunction_AS_FRAME_CONSTRUCTOR(func); + PyFunctionObject *f = (PyFunctionObject *)func; Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); assert(nargs >= 0); PyThreadState *tstate = _PyThreadState_GET(); assert(nargs == 0 || stack != NULL); - if (((PyCodeObject *)f->fc_code)->co_flags & CO_OPTIMIZED) { + if (((PyCodeObject *)f->func_code)->co_flags & CO_OPTIMIZED) { return _PyEval_Vector(tstate, f, NULL, stack, nargs, kwnames); } else { - return _PyEval_Vector(tstate, f, f->fc_globals, stack, nargs, kwnames); + return _PyEval_Vector(tstate, f, f->func_globals, stack, nargs, kwnames); } } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 09857c7fa00..2a283b3113c 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -5,6 +5,7 @@ #include "pycore_moduleobject.h" // _PyModule_GetDict() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_code.h" // CO_FAST_LOCAL, etc. +#include "pycore_function.h" // _PyFunction_FromConstructor() #include "frameobject.h" // PyFrameObject #include "pycore_frame.h" @@ -626,8 +627,7 @@ frame_dealloc(PyFrameObject *f) /* Don't clear code object until the end */ co = frame->f_code; frame->f_code = NULL; - Py_CLEAR(frame->f_globals); - Py_CLEAR(frame->f_builtins); + Py_CLEAR(frame->f_func); Py_CLEAR(frame->f_locals); PyObject **locals = _PyFrame_GetLocalsArray(frame); for (int i = 0; i < frame->stacktop; i++) { @@ -782,16 +782,16 @@ PyTypeObject PyFrame_Type = { _Py_IDENTIFIER(__builtins__); static InterpreterFrame * -allocate_heap_frame(PyFrameConstructor *con, PyObject *locals) +allocate_heap_frame(PyFunctionObject *func, PyObject *locals) { - PyCodeObject *code = (PyCodeObject *)con->fc_code; + PyCodeObject *code = (PyCodeObject *)func->func_code; int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE; InterpreterFrame *frame = (InterpreterFrame *)PyMem_Malloc(sizeof(PyObject *)*size); if (frame == NULL) { PyErr_NoMemory(); return NULL; } - _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); + _PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus); for (Py_ssize_t i = 0; i < code->co_nlocalsplus; i++) { frame->localsplus[i] = NULL; } @@ -872,7 +872,12 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, .fc_kwdefaults = NULL, .fc_closure = NULL }; - InterpreterFrame *frame = allocate_heap_frame(&desc, locals); + PyFunctionObject *func = _PyFunction_FromConstructor(&desc); + if (func == NULL) { + return NULL; + } + InterpreterFrame *frame = allocate_heap_frame(func, locals); + Py_DECREF(func); if (frame == NULL) { return NULL; } @@ -910,6 +915,18 @@ _PyFrame_FastToLocalsWithError(InterpreterFrame *frame) { } co = frame->f_code; fast = _PyFrame_GetLocalsArray(frame); + if (frame->f_lasti < 0 && _Py_OPCODE(co->co_firstinstr[0]) == COPY_FREE_VARS) { + /* Free vars have not been initialized -- Do that */ + PyCodeObject *co = frame->f_code; + PyObject *closure = frame->f_func->func_closure; + int offset = co->co_nlocals + co->co_nplaincellvars; + for (int i = 0; i < co->co_nfreevars; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + Py_INCREF(o); + frame->localsplus[offset + i] = o; + } + frame->f_lasti = 0; + } for (int i = 0; i < co->co_nlocalsplus; i++) { _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); @@ -929,8 +946,7 @@ _PyFrame_FastToLocalsWithError(InterpreterFrame *frame) { PyObject *value = fast[i]; if (frame->f_state != FRAME_CLEARED) { if (kind & CO_FAST_FREE) { - // The cell was set when the frame was created from - // the function's closure. + // The cell was set by COPY_FREE_VARS. assert(value != NULL && PyCell_Check(value)); value = PyCell_GET(value); } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 5a170380cb3..7891e4f3122 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -9,6 +9,39 @@ static uint32_t next_func_version = 1; +PyFunctionObject * +_PyFunction_FromConstructor(PyFrameConstructor *constr) +{ + + PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); + if (op == NULL) { + return NULL; + } + Py_INCREF(constr->fc_globals); + op->func_globals = constr->fc_globals; + Py_INCREF(constr->fc_builtins); + op->func_builtins = constr->fc_builtins; + Py_INCREF(constr->fc_name); + op->func_name = constr->fc_name; + Py_INCREF(constr->fc_qualname); + op->func_qualname = constr->fc_qualname; + Py_INCREF(constr->fc_code); + op->func_code = constr->fc_code; + op->func_defaults = NULL; + op->func_kwdefaults = NULL; + op->func_closure = NULL; + Py_INCREF(Py_None); + op->func_doc = Py_None; + op->func_dict = NULL; + op->func_weakreflist = NULL; + op->func_module = NULL; + op->func_annotations = NULL; + op->vectorcall = _PyFunction_Vectorcall; + op->func_version = 0; + _PyObject_GC_TRACK(op); + return op; +} + PyObject * PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) { diff --git a/Objects/genobject.c b/Objects/genobject.c index c899ed6a82e..24d5f3579cb 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -188,7 +188,6 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, } assert(_PyFrame_IsRunnable(frame)); - assert(frame->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START); /* Push arg onto the frame's value stack */ result = arg ? arg : Py_None; Py_INCREF(result); @@ -841,7 +840,7 @@ PyTypeObject PyGen_Type = { }; static PyObject * -make_gen(PyTypeObject *type, PyFrameConstructor *con, InterpreterFrame *frame) +make_gen(PyTypeObject *type, PyFunctionObject *func, InterpreterFrame *frame) { PyGenObject *gen = PyObject_GC_New(PyGenObject, type); if (gen == NULL) { @@ -858,13 +857,13 @@ make_gen(PyTypeObject *type, PyFrameConstructor *con, InterpreterFrame *frame) gen->gi_exc_state.exc_value = NULL; gen->gi_exc_state.exc_traceback = NULL; gen->gi_exc_state.previous_item = NULL; - if (con->fc_name != NULL) - gen->gi_name = con->fc_name; + if (func->func_name != NULL) + gen->gi_name = func->func_name; else gen->gi_name = gen->gi_code->co_name; Py_INCREF(gen->gi_name); - if (con->fc_qualname != NULL) - gen->gi_qualname = con->fc_qualname; + if (func->func_qualname != NULL) + gen->gi_qualname = func->func_qualname; else gen->gi_qualname = gen->gi_name; Py_INCREF(gen->gi_qualname); @@ -876,17 +875,17 @@ static PyObject * compute_cr_origin(int origin_depth); PyObject * -_Py_MakeCoro(PyFrameConstructor *con, InterpreterFrame *frame) +_Py_MakeCoro(PyFunctionObject *func, InterpreterFrame *frame) { - int coro_flags = ((PyCodeObject *)con->fc_code)->co_flags & + int coro_flags = ((PyCodeObject *)func->func_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR); assert(coro_flags); if (coro_flags == CO_GENERATOR) { - return make_gen(&PyGen_Type, con, frame); + return make_gen(&PyGen_Type, func, frame); } if (coro_flags == CO_ASYNC_GENERATOR) { PyAsyncGenObject *o; - o = (PyAsyncGenObject *)make_gen(&PyAsyncGen_Type, con, frame); + o = (PyAsyncGenObject *)make_gen(&PyAsyncGen_Type, func, frame); if (o == NULL) { return NULL; } @@ -897,7 +896,7 @@ _Py_MakeCoro(PyFrameConstructor *con, InterpreterFrame *frame) return (PyObject*)o; } assert (coro_flags == CO_COROUTINE); - PyObject *coro = make_gen(&PyCoro_Type, con, frame); + PyObject *coro = make_gen(&PyCoro_Type, func, frame); if (!coro) { return NULL; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 4180a9d13c4..22e509be26f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8936,7 +8936,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co, // "firstarg" is a cell here unless (very unlikely) super() // was called from the C-API before the first MAKE_CELL op. if (f->f_frame->f_lasti >= 0) { - assert(_Py_OPCODE(*co->co_firstinstr) == MAKE_CELL); + assert(_Py_OPCODE(*co->co_firstinstr) == MAKE_CELL || _Py_OPCODE(*co->co_firstinstr) == COPY_FREE_VARS); assert(PyCell_Check(firstarg)); firstarg = PyCell_GET(firstarg); } diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index e1d59de7bc8..9d96f4bd536 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -193,6 +193,7 @@ + diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 9e3b25c59a5..6763f996970 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -212,9 +212,8 @@ builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs, Py_TYPE(ns)->tp_name); goto error; } - PyFrameConstructor *f = PyFunction_AS_FRAME_CONSTRUCTOR(func); PyThreadState *tstate = _PyThreadState_GET(); - cell = _PyEval_Vector(tstate, f, ns, NULL, 0, NULL); + cell = _PyEval_Vector(tstate, (PyFunctionObject *)func, ns, NULL, 0, NULL); if (cell != NULL) { if (bases != orig_bases) { if (PyMapping_SetItemString(ns, "__orig_bases__", orig_bases) < 0) { diff --git a/Python/ceval.c b/Python/ceval.c index 1d69708576f..0aec5aa7fb9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -14,6 +14,7 @@ #include "pycore_call.h" // _PyObject_FastCallDictTstate() #include "pycore_ceval.h" // _PyEval_SignalAsyncExc() #include "pycore_code.h" +#include "pycore_function.h" #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_object.h" // _PyObject_GC_TRACK() @@ -98,7 +99,7 @@ static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwarg static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int); static int get_exception_handler(PyCodeObject *, int, int*, int*, int*); static InterpreterFrame * -_PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, +_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames); static int @@ -1135,7 +1136,13 @@ PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals) .fc_kwdefaults = NULL, .fc_closure = NULL }; - return _PyEval_Vector(tstate, &desc, locals, NULL, 0, NULL); + PyFunctionObject *func = _PyFunction_FromConstructor(&desc); + if (func == NULL) { + return NULL; + } + PyObject *res = _PyEval_Vector(tstate, func, locals, NULL, 0, NULL); + Py_DECREF(func); + return res; } @@ -1570,7 +1577,7 @@ trace_function_entry(PyThreadState *tstate, InterpreterFrame *frame) } static PyObject * -make_coro(PyThreadState *tstate, PyFrameConstructor *con, +make_coro(PyThreadState *tstate, PyFunctionObject *func, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames); @@ -2240,7 +2247,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr if (new_frame == NULL) { goto error; } - _PyFrame_InitializeSpecials(new_frame, PyFunction_AS_FRAME_CONSTRUCTOR(getitem), + _PyFrame_InitializeSpecials(new_frame, getitem, NULL, code->co_nlocalsplus); STACK_SHRINK(2); new_frame->localsplus[0] = container; @@ -3179,6 +3186,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr DISPATCH(); } + TARGET(COPY_FREE_VARS) { + /* Copy closure variables to free variables */ + PyCodeObject *co = frame->f_code; + PyObject *closure = frame->f_func->func_closure; + int offset = co->co_nlocals + co->co_nplaincellvars; + assert(oparg == co->co_nfreevars); + for (int i = 0; i < oparg; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + Py_INCREF(o); + frame->localsplus[offset + i] = o; + } + DISPATCH(); + } + TARGET(BUILD_STRING) { PyObject *str; PyObject *empty = PyUnicode_New(0, 0); @@ -4423,9 +4444,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : PyFunction_GET_GLOBALS(function); STACK_SHRINK(oparg); InterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, PyFunction_AS_FRAME_CONSTRUCTOR(function), locals, - stack_pointer, - nargs, kwnames); + tstate, (PyFunctionObject *)function, locals, + stack_pointer, nargs, kwnames + ); STACK_SHRINK(postcall_shrink); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. @@ -4506,7 +4527,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr if (new_frame == NULL) { goto error; } - _PyFrame_InitializeSpecials(new_frame, PyFunction_AS_FRAME_CONSTRUCTOR(func), + _PyFrame_InitializeSpecials(new_frame, func, NULL, code->co_nlocalsplus); STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { @@ -5426,11 +5447,11 @@ get_exception_handler(PyCodeObject *code, int index, int *level, int *handler, i } static int -initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, +initialize_locals(PyThreadState *tstate, PyFunctionObject *func, PyObject **localsplus, PyObject *const *args, Py_ssize_t argcount, PyObject *kwnames) { - PyCodeObject *co = (PyCodeObject*)con->fc_code; + 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) */ @@ -5495,7 +5516,7 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, if (keyword == NULL || !PyUnicode_Check(keyword)) { _PyErr_Format(tstate, PyExc_TypeError, "%U() keywords must be strings", - con->fc_qualname); + func->func_qualname); goto kw_fail; } @@ -5527,14 +5548,14 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, if (co->co_posonlyargcount && positional_only_passed_as_keyword(tstate, co, kwcount, kwnames, - con->fc_qualname)) + func->func_qualname)) { goto kw_fail; } _PyErr_Format(tstate, PyExc_TypeError, "%U() got an unexpected keyword argument '%S'", - con->fc_qualname, keyword); + func->func_qualname, keyword); goto kw_fail; } @@ -5555,7 +5576,7 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, if (localsplus[j] != NULL) { _PyErr_Format(tstate, PyExc_TypeError, "%U() got multiple values for argument '%S'", - con->fc_qualname, keyword); + func->func_qualname, keyword); goto kw_fail; } localsplus[j] = value; @@ -5564,14 +5585,14 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, /* Check the number of positional arguments */ if ((argcount > co->co_argcount) && !(co->co_flags & CO_VARARGS)) { - too_many_positional(tstate, co, argcount, con->fc_defaults, localsplus, - con->fc_qualname); + too_many_positional(tstate, co, argcount, func->func_defaults, localsplus, + func->func_qualname); goto fail_post_args; } /* Add missing positional arguments (copy default values from defs) */ if (argcount < co->co_argcount) { - Py_ssize_t defcount = con->fc_defaults == NULL ? 0 : PyTuple_GET_SIZE(con->fc_defaults); + Py_ssize_t defcount = func->func_defaults == NULL ? 0 : PyTuple_GET_SIZE(func->func_defaults); Py_ssize_t m = co->co_argcount - defcount; Py_ssize_t missing = 0; for (i = argcount; i < m; i++) { @@ -5581,7 +5602,7 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, } if (missing) { missing_arguments(tstate, co, missing, defcount, localsplus, - con->fc_qualname); + func->func_qualname); goto fail_post_args; } if (n > m) @@ -5589,7 +5610,7 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, else i = 0; if (defcount) { - PyObject **defs = &PyTuple_GET_ITEM(con->fc_defaults, 0); + PyObject **defs = &PyTuple_GET_ITEM(func->func_defaults, 0); for (; i < defcount; i++) { if (localsplus[m+i] == NULL) { PyObject *def = defs[i]; @@ -5607,8 +5628,8 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, if (localsplus[i] != NULL) continue; PyObject *varname = PyTuple_GET_ITEM(co->co_localsplusnames, i); - if (con->fc_kwdefaults != NULL) { - PyObject *def = PyDict_GetItemWithError(con->fc_kwdefaults, varname); + if (func->func_kwdefaults != NULL) { + PyObject *def = PyDict_GetItemWithError(func->func_kwdefaults, varname); if (def) { Py_INCREF(def); localsplus[i] = def; @@ -5622,16 +5643,10 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, } if (missing) { missing_arguments(tstate, co, missing, -1, localsplus, - con->fc_qualname); + func->func_qualname); goto fail_post_args; } } - /* Copy closure variables to free variables */ - for (i = 0; i < co->co_nfreevars; ++i) { - PyObject *o = PyTuple_GET_ITEM(con->fc_closure, i); - Py_INCREF(o); - localsplus[co->co_nlocals + co->co_nplaincellvars + i] = o; - } return 0; fail_pre_positional: @@ -5653,24 +5668,24 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con, static InterpreterFrame * make_coro_frame(PyThreadState *tstate, - PyFrameConstructor *con, PyObject *locals, + PyFunctionObject *func, PyObject *locals, PyObject *const *args, Py_ssize_t argcount, PyObject *kwnames) { assert(is_tstate_valid(tstate)); - assert(con->fc_defaults == NULL || PyTuple_CheckExact(con->fc_defaults)); - PyCodeObject *code = (PyCodeObject *)con->fc_code; + assert(func->func_defaults == NULL || PyTuple_CheckExact(func->func_defaults)); + PyCodeObject *code = (PyCodeObject *)func->func_code; int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE; InterpreterFrame *frame = (InterpreterFrame *)PyMem_Malloc(sizeof(PyObject *)*size); if (frame == NULL) { goto fail_no_memory; } - _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); + _PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus); for (int i = 0; i < code->co_nlocalsplus; i++) { frame->localsplus[i] = NULL; } assert(frame->frame_obj == NULL); - if (initialize_locals(tstate, con, frame->localsplus, args, argcount, kwnames)) { + if (initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames)) { _PyFrame_Clear(frame, 1); return NULL; } @@ -5692,17 +5707,17 @@ make_coro_frame(PyThreadState *tstate, /* Consumes all the references to the args */ static PyObject * -make_coro(PyThreadState *tstate, PyFrameConstructor *con, +make_coro(PyThreadState *tstate, PyFunctionObject *func, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames) { - assert (((PyCodeObject *)con->fc_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)); - InterpreterFrame *frame = make_coro_frame(tstate, con, locals, args, argcount, kwnames); + assert (((PyCodeObject *)func->func_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)); + InterpreterFrame *frame = make_coro_frame(tstate, func, locals, args, argcount, kwnames); if (frame == NULL) { return NULL; } - PyObject *gen = _Py_MakeCoro(con, frame); + PyObject *gen = _Py_MakeCoro(func, frame); if (gen == NULL) { return NULL; } @@ -5711,22 +5726,22 @@ make_coro(PyThreadState *tstate, PyFrameConstructor *con, /* Consumes all the references to the args */ static InterpreterFrame * -_PyEvalFramePushAndInit(PyThreadState *tstate, PyFrameConstructor *con, +_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames) { - PyCodeObject * code = (PyCodeObject *)con->fc_code; + PyCodeObject * code = (PyCodeObject *)func->func_code; size_t size = code->co_nlocalsplus + code->co_stacksize + FRAME_SPECIALS_SIZE; InterpreterFrame *frame = _PyThreadState_BumpFramePointer(tstate, size); if (frame == NULL) { goto fail; } - _PyFrame_InitializeSpecials(frame, con, locals, code->co_nlocalsplus); + _PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus); PyObject **localsarray = &frame->localsplus[0]; for (int i = 0; i < code->co_nlocalsplus; i++) { localsarray[i] = NULL; } - if (initialize_locals(tstate, con, localsarray, args, argcount, kwnames)) { + if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) { _PyFrame_Clear(frame, 0); return NULL; } @@ -5761,12 +5776,12 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, InterpreterFrame * frame) } PyObject * -_PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, +_PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func, PyObject *locals, PyObject* const* args, size_t argcount, PyObject *kwnames) { - PyCodeObject *code = (PyCodeObject *)con->fc_code; + PyCodeObject *code = (PyCodeObject *)func->func_code; /* _PyEvalFramePushAndInit and make_coro consume * all the references to their arguments */ @@ -5782,10 +5797,10 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con, int is_coro = code->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR); if (is_coro) { - return make_coro(tstate, con, locals, args, argcount, kwnames); + return make_coro(tstate, func, locals, args, argcount, kwnames); } InterpreterFrame *frame = _PyEvalFramePushAndInit( - tstate, con, locals, args, argcount, kwnames); + tstate, func, locals, args, argcount, kwnames); if (frame == NULL) { return NULL; } @@ -5869,9 +5884,14 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, .fc_kwdefaults = kwdefs, .fc_closure = closure }; - res = _PyEval_Vector(tstate, &constr, locals, + PyFunctionObject *func = _PyFunction_FromConstructor(&constr); + if (func == NULL) { + return NULL; + } + res = _PyEval_Vector(tstate, func, locals, allargs, argcount, kwnames); + Py_DECREF(func); if (kwcount) { Py_DECREF(kwnames); PyMem_Free(newargs); diff --git a/Python/compile.c b/Python/compile.c index 40bd1fde0a9..87de7baab48 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1171,6 +1171,7 @@ stack_effect(int opcode, int oparg, int jump) /* Closures */ case MAKE_CELL: + case COPY_FREE_VARS: return 0; case LOAD_CLOSURE: return 1; @@ -7611,7 +7612,7 @@ insert_instruction(basicblock *block, int pos, struct instr *instr) { static int insert_prefix_instructions(struct compiler *c, basicblock *entryblock, - int *fixed) + int *fixed, int nfreevars) { int flags = compute_code_flags(c); @@ -7684,6 +7685,22 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock, } } + if (nfreevars) { + struct instr copy_frees = { + .i_opcode = COPY_FREE_VARS, + .i_oparg = nfreevars, + .i_lineno = -1, + .i_col_offset = -1, + .i_end_lineno = -1, + .i_end_col_offset = -1, + .i_target = NULL, + }; + if (insert_instruction(entryblock, 0, ©_frees) < 0) { + return -1; + } + + } + return 0; } @@ -7818,7 +7835,7 @@ assemble(struct compiler *c, int addNone) } // This must be called before fix_cell_offsets(). - if (insert_prefix_instructions(c, entryblock, cellfixedoffsets)) { + if (insert_prefix_instructions(c, entryblock, cellfixedoffsets, nfreevars)) { goto error; } diff --git a/Python/frame.c b/Python/frame.c index a5c93eaaa5f..79b0f77a065 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -8,9 +8,8 @@ int _PyFrame_Traverse(InterpreterFrame *frame, visitproc visit, void *arg) { Py_VISIT(frame->frame_obj); - Py_VISIT(frame->f_globals); - Py_VISIT(frame->f_builtins); Py_VISIT(frame->f_locals); + Py_VISIT(frame->f_func); Py_VISIT(frame->f_code); /* locals */ PyObject **locals = _PyFrame_GetLocalsArray(frame); @@ -62,8 +61,7 @@ clear_specials(InterpreterFrame *frame) frame->generator = NULL; Py_XDECREF(frame->frame_obj); Py_XDECREF(frame->f_locals); - Py_DECREF(frame->f_globals); - Py_DECREF(frame->f_builtins); + Py_DECREF(frame->f_func); Py_DECREF(frame->f_code); } diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 903b967c3a5..c9d430d2681 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -148,7 +148,7 @@ static void *opcode_targets[256] = { &&TARGET_SET_ADD, &&TARGET_MAP_ADD, &&TARGET_LOAD_CLASSDEREF, - &&_unknown_opcode, + &&TARGET_COPY_FREE_VARS, &&_unknown_opcode, &&_unknown_opcode, &&TARGET_MATCH_CLASS, diff --git a/Python/pystate.c b/Python/pystate.c index 273982b4bd2..56db095d24b 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2087,9 +2087,9 @@ _PyThreadState_BumpFramePointerSlow(PyThreadState *tstate, size_t size) InterpreterFrame * -_PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals) +_PyThreadState_PushFrame(PyThreadState *tstate, PyFunctionObject *func, PyObject *locals) { - PyCodeObject *code = (PyCodeObject *)con->fc_code; + PyCodeObject *code = (PyCodeObject *)func->func_code; int nlocalsplus = code->co_nlocalsplus; size_t size = nlocalsplus + code->co_stacksize + FRAME_SPECIALS_SIZE; @@ -2097,7 +2097,7 @@ _PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObjec if (frame == NULL) { return NULL; } - _PyFrame_InitializeSpecials(frame, con, locals, nlocalsplus); + _PyFrame_InitializeSpecials(frame, func, locals, nlocalsplus); for (int i=0; i < nlocalsplus; i++) { frame->localsplus[i] = NULL; } diff --git a/Python/specialize.c b/Python/specialize.c index 130da008ad8..f5f12139df7 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -479,7 +479,7 @@ initial_counter_value(void) { #define SPEC_FAIL_WRONG_NUMBER_ARGUMENTS 9 #define SPEC_FAIL_CO_NOT_OPTIMIZED 10 /* SPEC_FAIL_METHOD defined as 11 above */ -#define SPEC_FAIL_FREE_VARS 12 + #define SPEC_FAIL_PYCFUNCTION 13 #define SPEC_FAIL_PYCFUNCTION_WITH_KEYWORDS 14 #define SPEC_FAIL_PYCFUNCTION_FAST_WITH_KEYWORDS 15 @@ -1158,9 +1158,6 @@ function_kind(PyCodeObject *code) { if ((flags & CO_OPTIMIZED) == 0) { return SPEC_FAIL_CO_NOT_OPTIMIZED; } - if (code->co_nfreevars) { - return SPEC_FAIL_FREE_VARS; - } return SIMPLE_FUNCTION; }