Skip to content

bpo-44525: Split calls into PRECALL and CALL #30011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 15 additions & 32 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ the following command can be used to display the disassembly of
>>> dis.dis(myfunc)
2 0 LOAD_GLOBAL 0 (len)
2 LOAD_FAST 0 (alist)
4 CALL_FUNCTION 1
4 CALL_NO_KW 1
6 RETURN_VALUE

(The "2" is a line number).
Expand Down Expand Up @@ -104,7 +104,7 @@ Example::
...
LOAD_GLOBAL
LOAD_FAST
CALL_FUNCTION
CALL_NO_KW
RETURN_VALUE


Expand Down Expand Up @@ -616,7 +616,7 @@ iterations of the loop.
.. opcode:: LOAD_BUILD_CLASS

Pushes :func:`builtins.__build_class__` onto the stack. It is later called
by :opcode:`CALL_FUNCTION` to construct a class.
by :opcode:`CALL_NO_KW` to construct a class.


.. opcode:: BEFORE_WITH (delta)
Expand Down Expand Up @@ -1039,21 +1039,20 @@ All of the following opcodes use their arguments.
with ``__cause__`` set to ``TOS``)


.. opcode:: CALL_FUNCTION (argc)
.. opcode:: CALL_NO_KW (argc)

Calls a callable object with positional arguments.
*argc* indicates the number of positional arguments.
The top of the stack contains positional arguments, with the right-most
argument on top. Below the arguments is a callable object to call.
``CALL_FUNCTION`` pops all arguments and the callable object off the stack,
``CALL_NO_KW`` pops all arguments and the callable object off the stack,
calls the callable object with those arguments, and pushes the return value
returned by the callable object.

.. versionchanged:: 3.6
This opcode is used only for calls with positional arguments.
.. versionadded:: 3.11


.. opcode:: CALL_FUNCTION_KW (argc)
.. opcode:: CALL_KW (argc)

Calls a callable object with positional (if any) and keyword arguments.
*argc* indicates the total number of positional and keyword arguments.
Expand All @@ -1063,13 +1062,11 @@ All of the following opcodes use their arguments.
in the order corresponding to the tuple.
Below that are positional arguments, with the right-most parameter on
top. Below the arguments is a callable object to call.
``CALL_FUNCTION_KW`` pops all arguments and the callable object off the stack,
``CALL_KW`` pops all arguments and the callable object off the stack,
calls the callable object with those arguments, and pushes the return value
returned by the callable object.

.. versionchanged:: 3.6
Keyword arguments are packed in a tuple instead of a dictionary,
*argc* indicates the total number of arguments.
.. versionadded:: 3.11


.. opcode:: CALL_FUNCTION_EX (flags)
Expand Down Expand Up @@ -1099,30 +1096,16 @@ All of the following opcodes use their arguments.
.. versionadded:: 3.7


.. opcode:: CALL_METHOD (argc)

Calls a method. *argc* is the number of positional arguments.
Keyword arguments are not supported. This opcode is designed to be used
with :opcode:`LOAD_METHOD`. Positional arguments are on top of the stack.
Below them, the two items described in :opcode:`LOAD_METHOD` are on the
stack (either ``self`` and an unbound method object or ``NULL`` and an
arbitrary callable). All of them are popped and the return value is pushed.

.. versionadded:: 3.7


.. opcode:: CALL_METHOD_KW (argc)
.. opcode:: PRECALL_METHOD (argc)

Calls a method in a similar fashion as :opcode:`CALL_METHOD`, but also supports keyword arguments.
*argc* is the number of positional and keyword arguments.
This opcode is designed to be used with :opcode:`LOAD_METHOD`. TOS is a
tuple of keyword argument names. Argument values are below that.
Below them, the two items described in :opcode:`LOAD_METHOD` are on the
stack (either ``self`` and an unbound method object or ``NULL`` and an
arbitrary callable). All of them are popped from the stack and the return value is pushed.
Prefixes either :opcode:`CALL_NO_KW` or :opcode:`CALL_KW`.
This opcode is designed to be used with :opcode:`LOAD_METHOD`.
Sets internal variables, so that :opcode:`CALL_NO_KW` or :opcode:`CALL_KW`
clean up after :opcode:`LOAD_METHOD` correctly.

.. versionadded:: 3.11


.. opcode:: MAKE_FUNCTION (flags)

Pushes a new function object on the stack. From bottom to top, the consumed
Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ int _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins, _Py_CODEUNI
int _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache);
int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache);
int _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *instr);
int _Py_Specialize_CallFunction(PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, PyObject *builtins);
int _Py_Specialize_CallNoKw(PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, PyObject *builtins);
void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
SpecializedCacheEntry *cache);
void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache);
Expand All @@ -288,7 +288,7 @@ void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
#define COLLECT_SPECIALIZATION_STATS_DETAILED PRINT_SPECIALIZATION_STATS_DETAILED
#endif

#define SPECIALIZATION_FAILURE_KINDS 20
#define SPECIALIZATION_FAILURE_KINDS 30

#if COLLECT_SPECIALIZATION_STATS

Expand Down
68 changes: 35 additions & 33 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 @@ -372,6 +372,7 @@ def _write_atomic(path, data, mode=0o666):
# BINARY_OP)
# 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)

#
# MAGIC must change whenever the bytecode emitted by the compiler may no
Expand All @@ -381,7 +382,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 = (3466).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3467).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

_PYCACHE = '__pycache__'
Expand Down
25 changes: 15 additions & 10 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def jabs_op(name, op):

def_op('GEN_START', 129) # Kind of generator/coroutine
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
def_op('CALL_FUNCTION', 131) # #args

def_op('MAKE_FUNCTION', 132) # Flags
def_op('BUILD_SLICE', 133) # Number of items

Expand All @@ -170,7 +170,6 @@ def jabs_op(name, op):
def_op('DELETE_DEREF', 139)
hasfree.append(139)

def_op('CALL_FUNCTION_KW', 141) # #args + #kwargs
def_op('CALL_FUNCTION_EX', 142) # Flags

def_op('EXTENDED_ARG', 144)
Expand All @@ -189,12 +188,15 @@ def jabs_op(name, op):
def_op('BUILD_STRING', 157)

name_op('LOAD_METHOD', 160)
def_op('CALL_METHOD', 161)

def_op('LIST_EXTEND', 162)
def_op('SET_UPDATE', 163)
def_op('DICT_MERGE', 164)
def_op('DICT_UPDATE', 165)
def_op('CALL_METHOD_KW', 166)

def_op('PRECALL_METHOD', 168)
def_op('CALL_NO_KW', 169)
def_op('CALL_KW', 170)

del def_op, name_op, jrel_op, jabs_op

Expand Down Expand Up @@ -249,12 +251,15 @@ def jabs_op(name, op):
"STORE_SUBSCR_ADAPTIVE",
"STORE_SUBSCR_LIST_INT",
"STORE_SUBSCR_DICT",
"CALL_FUNCTION_ADAPTIVE",
"CALL_FUNCTION_BUILTIN_O",
"CALL_FUNCTION_BUILTIN_FAST",
"CALL_FUNCTION_LEN",
"CALL_FUNCTION_ISINSTANCE",
"CALL_FUNCTION_PY_SIMPLE",
"CALL_NO_KW_ADAPTIVE",
"CALL_NO_KW_BUILTIN_O",
"CALL_NO_KW_BUILTIN_FAST",
"CALL_NO_KW_LEN",
"CALL_NO_KW_ISINSTANCE",
"CALL_NO_KW_PY_SIMPLE",
"CALL_NO_KW_LIST_APPEND",
"CALL_NO_KW_METHOD_DESCRIPTOR_O",
"CALL_NO_KW_METHOD_DESCRIPTOR_FAST",
"JUMP_ABSOLUTE_QUICK",
"LOAD_ATTR_ADAPTIVE",
"LOAD_ATTR_INSTANCE_VALUE",
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ def foo(x):
self.assertNotIn('LOAD_METHOD', instructions)
self.assertNotIn('CALL_METHOD', instructions)
self.assertIn('LOAD_ATTR', instructions)
self.assertIn('CALL_FUNCTION', instructions)
self.assertIn('CALL_NO_KW', instructions)

def test_lineno_procedure_call(self):
def call():
Expand Down Expand Up @@ -1095,7 +1095,7 @@ def test_multiline_expression(self):
)
"""
compiled_code, _ = self.check_positions_against_ast(snippet)
self.assertOpcodeSourcePositionIs(compiled_code, 'CALL_FUNCTION',
self.assertOpcodeSourcePositionIs(compiled_code, 'CALL_NO_KW',
line=1, end_line=3, column=0, end_column=1)

def test_very_long_line_end_offset(self):
Expand All @@ -1105,7 +1105,7 @@ def test_very_long_line_end_offset(self):
snippet = f"g('{long_string}')"

compiled_code, _ = self.check_positions_against_ast(snippet)
self.assertOpcodeSourcePositionIs(compiled_code, 'CALL_FUNCTION',
self.assertOpcodeSourcePositionIs(compiled_code, 'CALL_NO_KW',
line=1, end_line=1, column=None, end_column=None)

def test_complex_single_line_expression(self):
Expand Down
Loading