Skip to content

Commit 89fd7c3

Browse files
authored
bpo-46329: Split calls into precall and call instructions. (GH-30855)
* Add PRECALL_FUNCTION opcode. * Move 'call shape' varaibles into struct. * Replace CALL_NO_KW and CALL_KW with KW_NAMES and CALL instructions. * Specialize for builtin methods taking using the METH_FASTCALL | METH_KEYWORDS protocol. * Allow kwnames for specialized calls to builtin types. * Specialize calls to tuple(arg) and str(arg).
1 parent 5a9e423 commit 89fd7c3

File tree

16 files changed

+912
-629
lines changed

16 files changed

+912
-629
lines changed

Doc/library/dis.rst

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,13 @@ the following command can be used to display the disassembly of
3434
:func:`myfunc`::
3535

3636
>>> dis.dis(myfunc)
37-
2 0 LOAD_GLOBAL 0 (len)
38-
2 LOAD_FAST 0 (alist)
39-
4 CALL_NO_KW 1
40-
6 RETURN_VALUE
37+
1 0 RESUME 0
38+
39+
2 2 LOAD_GLOBAL 0 (len)
40+
4 LOAD_FAST 0 (alist)
41+
6 PRECALL_FUNCTION 1
42+
8 CALL 0
43+
10 RETURN_VALUE
4144

4245
(The "2" is a line number).
4346

@@ -102,9 +105,11 @@ Example::
102105
>>> for instr in bytecode:
103106
... print(instr.opname)
104107
...
108+
RESUME
105109
LOAD_GLOBAL
106110
LOAD_FAST
107-
CALL_NO_KW
111+
PRECALL_FUNCTION
112+
CALL
108113
RETURN_VALUE
109114

110115

@@ -617,7 +622,7 @@ iterations of the loop.
617622
.. opcode:: LOAD_BUILD_CLASS
618623

619624
Pushes :func:`builtins.__build_class__` onto the stack. It is later called
620-
by :opcode:`CALL_NO_KW` to construct a class.
625+
to construct a class.
621626

622627

623628
.. opcode:: BEFORE_WITH (delta)
@@ -1058,30 +1063,19 @@ iterations of the loop.
10581063
with ``__cause__`` set to ``TOS``)
10591064

10601065

1061-
.. opcode:: CALL_NO_KW (argc)
1062-
1063-
Calls a callable object with positional arguments.
1064-
*argc* indicates the number of positional arguments.
1065-
The top of the stack contains positional arguments, with the right-most
1066-
argument on top. Below the arguments is a callable object to call.
1067-
``CALL_NO_KW`` pops all arguments and the callable object off the stack,
1068-
calls the callable object with those arguments, and pushes the return value
1069-
returned by the callable object.
1070-
1071-
.. versionadded:: 3.11
1066+
.. opcode:: CALL (named)
10721067

1068+
Calls a callable object with the number of positional arguments specified by
1069+
the preceding :opcode:`PRECALL_FUNCTION` or :opcode:`PRECALL_METHOD` and
1070+
the named arguments specified by the preceding :opcode:`KW_NAMES`, if any.
1071+
*named* indicates the number of named arguments.
1072+
On the stack are (in ascending order):
10731073

1074-
.. opcode:: CALL_KW (argc)
1074+
* The callable
1075+
* The positional arguments
1076+
* The named arguments
10751077

1076-
Calls a callable object with positional (if any) and keyword arguments.
1077-
*argc* indicates the total number of positional and keyword arguments.
1078-
The top element on the stack contains a tuple with the names of the
1079-
keyword arguments, which must be strings.
1080-
Below that are the values for the keyword arguments,
1081-
in the order corresponding to the tuple.
1082-
Below that are positional arguments, with the right-most parameter on
1083-
top. Below the arguments is a callable object to call.
1084-
``CALL_KW`` pops all arguments and the callable object off the stack,
1078+
``CALL`` pops all arguments and the callable object off the stack,
10851079
calls the callable object with those arguments, and pushes the return value
10861080
returned by the callable object.
10871081

@@ -1108,7 +1102,7 @@ iterations of the loop.
11081102
Loads a method named ``co_names[namei]`` from the TOS object. TOS is popped.
11091103
This bytecode distinguishes two cases: if TOS has a method with the correct
11101104
name, the bytecode pushes the unbound method and TOS. TOS will be used as
1111-
the first argument (``self``) by :opcode:`CALL_METHOD` when calling the
1105+
the first argument (``self``) by :opcode:`PRECALL_METHOD` when calling the
11121106
unbound method. Otherwise, ``NULL`` and the object return by the attribute
11131107
lookup are pushed.
11141108

@@ -1117,14 +1111,30 @@ iterations of the loop.
11171111

11181112
.. opcode:: PRECALL_METHOD (argc)
11191113

1120-
Prefixes either :opcode:`CALL_NO_KW` or :opcode:`CALL_KW`.
1114+
Prefixes :opcode:`CALL` (possibly with an intervening ``KW_NAMES``).
11211115
This opcode is designed to be used with :opcode:`LOAD_METHOD`.
1122-
Sets internal variables, so that :opcode:`CALL_NO_KW` or :opcode:`CALL_KW`
1116+
Sets internal variables, so that :opcode:`CALL`
11231117
clean up after :opcode:`LOAD_METHOD` correctly.
11241118

11251119
.. versionadded:: 3.11
11261120

11271121

1122+
.. opcode:: PRECALL_FUNCTION (args)
1123+
1124+
Prefixes :opcode:`CALL` (possibly with an intervening ``KW_NAMES``).
1125+
Sets internal variables, so that :opcode:`CALL` can execute correctly.
1126+
1127+
.. versionadded:: 3.11
1128+
1129+
1130+
.. opcode:: KW_NAMES (i)
1131+
1132+
Stores a reference to ``co_consts[consti]`` into an internal variable
1133+
for use by :opcode:`CALL`. ``co_consts[consti]`` must be a tuple of strings.
1134+
1135+
.. versionadded:: 3.11
1136+
1137+
11281138
.. opcode:: MAKE_FUNCTION (flags)
11291139

11301140
Pushes a new function object on the stack. From bottom to top, the consumed

Doc/whatsnew/3.11.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ CPython bytecode changes
400400

401401
* Replaced the three call instructions: :opcode:`CALL_FUNCTION`,
402402
:opcode:`CALL_FUNCTION_KW` and :opcode:`CALL_METHOD` with
403-
:opcode:`CALL_NO_KW`, :opcode:`CALL_KW` and :opcode:`PRECALL_METHOD`.
403+
:opcode:`PRECALL_FUNCTION`, :opcode:`PRECALL_METHOD`, :opcode:`CALL`,
404+
and :opcode:`KW_NAMES`.
404405
This decouples the argument shifting for methods from the handling of
405406
keyword arguments and allows better specialization of calls.
406407

Include/internal/pycore_code.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ typedef struct {
3838

3939
typedef struct {
4040
uint32_t func_version;
41-
uint16_t defaults_start;
41+
uint16_t min_args;
4242
uint16_t defaults_len;
4343
} _PyCallCache;
4444

@@ -271,7 +271,8 @@ int _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins, _Py_CODEUNI
271271
int _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache);
272272
int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache);
273273
int _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *instr);
274-
int _Py_Specialize_CallNoKw(PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, PyObject *builtins);
274+
int _Py_Specialize_CallNoKw(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
275+
PyObject *kwnames, SpecializedCacheEntry *cache, PyObject *builtins);
275276
void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
276277
SpecializedCacheEntry *cache);
277278
void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache);

Include/internal/pycore_frame.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ _PyThreadState_BumpFramePointer(PyThreadState *tstate, size_t size)
189189

190190
void _PyThreadState_PopFrame(PyThreadState *tstate, InterpreterFrame *frame);
191191

192+
InterpreterFrame *
193+
_PyFrame_Push(PyThreadState *tstate, PyFunctionObject *func);
194+
192195
#ifdef __cplusplus
193196
}
194197
#endif

Include/opcode.h

Lines changed: 43 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,9 +384,14 @@ def _write_atomic(path, data, mode=0o666):
384384
# Python 3.11a5 3476 (Add ASYNC_GEN_WRAP opcode)
385385
# Python 3.11a5 3477 (Replace DUP_TOP/DUP_TOP_TWO with COPY and
386386
# ROT_TWO/ROT_THREE/ROT_FOUR/ROT_N with SWAP)
387+
# Python 3.11a5 3478 (New CALL opcodes)
387388

388389
# Python 3.12 will start with magic number 3500
389390

391+
392+
# Python 3.12 will start with magic number 3500
393+
394+
390395
#
391396
# MAGIC must change whenever the bytecode emitted by the compiler may no
392397
# longer be understood by older implementations of the eval loop (usually
@@ -397,7 +402,7 @@ def _write_atomic(path, data, mode=0o666):
397402
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
398403
# in PC/launcher.c must also be updated.
399404

400-
MAGIC_NUMBER = (3477).to_bytes(2, 'little') + b'\r\n'
405+
MAGIC_NUMBER = (3478).to_bytes(2, 'little') + b'\r\n'
401406
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
402407

403408
_PYCACHE = '__pycache__'

Lib/opcode.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,12 @@ def jabs_op(name, op):
188188
def_op('DICT_MERGE', 164)
189189
def_op('DICT_UPDATE', 165)
190190

191+
def_op('PRECALL_FUNCTION', 167)
191192
def_op('PRECALL_METHOD', 168)
192-
def_op('CALL_NO_KW', 169)
193-
def_op('CALL_KW', 170)
193+
194+
def_op('CALL', 171)
195+
def_op('KW_NAMES', 172)
196+
hasconst.append(172)
194197

195198
del def_op, name_op, jrel_op, jabs_op
196199

@@ -245,16 +248,21 @@ def jabs_op(name, op):
245248
"STORE_SUBSCR_ADAPTIVE",
246249
"STORE_SUBSCR_LIST_INT",
247250
"STORE_SUBSCR_DICT",
248-
"CALL_NO_KW_ADAPTIVE",
251+
"CALL_ADAPTIVE",
252+
"CALL_BUILTIN_CLASS",
249253
"CALL_NO_KW_BUILTIN_O",
250254
"CALL_NO_KW_BUILTIN_FAST",
255+
"CALL_BUILTIN_FAST_WITH_KEYWORDS",
251256
"CALL_NO_KW_LEN",
252257
"CALL_NO_KW_ISINSTANCE",
253-
"CALL_NO_KW_PY_SIMPLE",
258+
"CALL_PY_EXACT_ARGS",
259+
"CALL_PY_WITH_DEFAULTS",
254260
"CALL_NO_KW_LIST_APPEND",
255261
"CALL_NO_KW_METHOD_DESCRIPTOR_O",
262+
"CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS",
263+
"CALL_NO_KW_STR_1",
264+
"CALL_NO_KW_TUPLE_1",
256265
"CALL_NO_KW_TYPE_1",
257-
"CALL_NO_KW_BUILTIN_CLASS_1",
258266
"CALL_NO_KW_METHOD_DESCRIPTOR_FAST",
259267
"JUMP_ABSOLUTE_QUICK",
260268
"LOAD_ATTR_ADAPTIVE",

Lib/test/test_compile.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -837,9 +837,8 @@ def foo(x):
837837
opcodes = list(dis.get_instructions(func))
838838
instructions = [opcode.opname for opcode in opcodes]
839839
self.assertNotIn('LOAD_METHOD', instructions)
840-
self.assertNotIn('CALL_METHOD', instructions)
841840
self.assertIn('LOAD_ATTR', instructions)
842-
self.assertIn('CALL_NO_KW', instructions)
841+
self.assertIn('PRECALL_FUNCTION', instructions)
843842

844843
def test_lineno_procedure_call(self):
845844
def call():
@@ -1096,7 +1095,7 @@ def test_multiline_expression(self):
10961095
)
10971096
"""
10981097
compiled_code, _ = self.check_positions_against_ast(snippet)
1099-
self.assertOpcodeSourcePositionIs(compiled_code, 'CALL_NO_KW',
1098+
self.assertOpcodeSourcePositionIs(compiled_code, 'CALL',
11001099
line=1, end_line=3, column=0, end_column=1)
11011100

11021101
def test_very_long_line_end_offset(self):
@@ -1106,7 +1105,7 @@ def test_very_long_line_end_offset(self):
11061105
snippet = f"g('{long_string}')"
11071106

11081107
compiled_code, _ = self.check_positions_against_ast(snippet)
1109-
self.assertOpcodeSourcePositionIs(compiled_code, 'CALL_NO_KW',
1108+
self.assertOpcodeSourcePositionIs(compiled_code, 'CALL',
11101109
line=1, end_line=1, column=None, end_column=None)
11111110

11121111
def test_complex_single_line_expression(self):

0 commit comments

Comments
 (0)