Skip to content

Commit b2bf2bc

Browse files
authored
bpo-43693: Compute deref offsets in compiler (gh-25152)
Merges locals and cells into a single array. Saves a pointer in the interpreter and means that we don't need the LOAD_CLOSURE opcode any more https://bugs.python.org/issue43693
1 parent 35002aa commit b2bf2bc

File tree

11 files changed

+222
-209
lines changed

11 files changed

+222
-209
lines changed

Doc/library/dis.rst

+23-6
Original file line numberDiff line numberDiff line change
@@ -1058,16 +1058,24 @@ All of the following opcodes use their arguments.
10581058

10591059
.. opcode:: LOAD_CLOSURE (i)
10601060

1061-
Pushes a reference to the cell contained in slot *i* of the cell and free
1062-
variable storage. The name of the variable is
1063-
``co_fastlocalnames[i + len(co_varnames)]``.
1061+
Pushes a reference to the cell contained in slot ``i`` of the "fast locals"
1062+
storage. The name of the variable is ``co_fastlocalnames[i]``.
1063+
1064+
Note that ``LOAD_CLOSURE`` is effectively an alias for ``LOAD_FAST``.
1065+
It exists to keep bytecode a little more readable.
1066+
1067+
.. versionchanged:: 3.11
1068+
``i`` is no longer offset by the length of ``co_varnames``.
10641069

10651070

10661071
.. opcode:: LOAD_DEREF (i)
10671072

1068-
Loads the cell contained in slot *i* of the cell and free variable storage.
1073+
Loads the cell contained in slot ``i`` of the "fast locals" storage.
10691074
Pushes a reference to the object the cell contains on the stack.
10701075

1076+
.. versionchanged:: 3.11
1077+
``i`` is no longer offset by the length of ``co_varnames``.
1078+
10711079

10721080
.. opcode:: LOAD_CLASSDEREF (i)
10731081

@@ -1077,20 +1085,29 @@ All of the following opcodes use their arguments.
10771085

10781086
.. versionadded:: 3.4
10791087

1088+
.. versionchanged:: 3.11
1089+
``i`` is no longer offset by the length of ``co_varnames``.
1090+
10801091

10811092
.. opcode:: STORE_DEREF (i)
10821093

1083-
Stores TOS into the cell contained in slot *i* of the cell and free variable
1094+
Stores TOS into the cell contained in slot ``i`` of the "fast locals"
10841095
storage.
10851096

1097+
.. versionchanged:: 3.11
1098+
``i`` is no longer offset by the length of ``co_varnames``.
1099+
10861100

10871101
.. opcode:: DELETE_DEREF (i)
10881102

1089-
Empties the cell contained in slot *i* of the cell and free variable storage.
1103+
Empties the cell contained in slot ``i`` of the "fast locals" storage.
10901104
Used by the :keyword:`del` statement.
10911105

10921106
.. versionadded:: 3.2
10931107

1108+
.. versionchanged:: 3.11
1109+
``i`` is no longer offset by the length of ``co_varnames``.
1110+
10941111

10951112
.. opcode:: RAISE_VARARGS (argc)
10961113

Lib/dis.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -378,14 +378,11 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
378378
elif op in hasjrel:
379379
argval = offset + 2 + arg*2
380380
argrepr = "to " + repr(argval)
381-
elif op in haslocal:
381+
elif op in haslocal or op in hasfree:
382382
argval, argrepr = _get_name_info(arg, varname_from_oparg)
383383
elif op in hascompare:
384384
argval = cmp_op[arg]
385385
argrepr = argval
386-
elif op in hasfree:
387-
argval, argrepr = _get_name_info(arg, varname_from_oparg,
388-
cell=True)
389386
elif op == FORMAT_VALUE:
390387
argval, argrepr = FORMAT_VALUE_CONVERTERS[arg & 0x3]
391388
argval = (argval, bool(arg & 0x4))

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ def _write_atomic(path, data, mode=0o666):
356356
# Python 3.11a1 3451 (Add CALL_METHOD_KW)
357357
# Python 3.11a1 3452 (drop nlocals from marshaled code objects)
358358
# Python 3.11a1 3453 (add co_fastlocalnames and co_fastlocalkinds)
359+
# Python 3.11a1 3454 (compute cell offsets relative to locals bpo-43693)
359360

360361
#
361362
# MAGIC must change whenever the bytecode emitted by the compiler may no
@@ -365,7 +366,7 @@ def _write_atomic(path, data, mode=0o666):
365366
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
366367
# in PC/launcher.c must also be updated.
367368

368-
MAGIC_NUMBER = (3453).to_bytes(2, 'little') + b'\r\n'
369+
MAGIC_NUMBER = (3454).to_bytes(2, 'little') + b'\r\n'
369370
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
370371

371372
_PYCACHE = '__pycache__'

Lib/test/test_dis.py

+20-20
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ def foo(x):
427427
return foo
428428

429429
dis_nested_0 = """\
430-
%3d 0 LOAD_CLOSURE 0 (y)
430+
%3d 0 LOAD_CLOSURE 2 (y)
431431
2 BUILD_TUPLE 1
432432
4 LOAD_CONST 1 (<code object foo at 0x..., file "%s", line %d>)
433433
6 LOAD_CONST 2 ('_h.<locals>.foo')
@@ -444,12 +444,12 @@ def foo(x):
444444

445445
dis_nested_1 = """%s
446446
Disassembly of <code object foo at 0x..., file "%s", line %d>:
447-
%3d 0 LOAD_CLOSURE 0 (x)
447+
%3d 0 LOAD_CLOSURE 1 (x)
448448
2 BUILD_TUPLE 1
449449
4 LOAD_CONST 1 (<code object <listcomp> at 0x..., file "%s", line %d>)
450450
6 LOAD_CONST 2 ('_h.<locals>.foo.<locals>.<listcomp>')
451451
8 MAKE_FUNCTION 8 (closure)
452-
10 LOAD_DEREF 1 (y)
452+
10 LOAD_DEREF 2 (y)
453453
12 GET_ITER
454454
14 CALL_FUNCTION 1
455455
16 RETURN_VALUE
@@ -467,7 +467,7 @@ def foo(x):
467467
2 LOAD_FAST 0 (.0)
468468
>> 4 FOR_ITER 6 (to 18)
469469
6 STORE_FAST 1 (z)
470-
8 LOAD_DEREF 0 (x)
470+
8 LOAD_DEREF 2 (x)
471471
10 LOAD_FAST 1 (z)
472472
12 BINARY_ADD
473473
14 LIST_APPEND 2
@@ -962,16 +962,16 @@ def jumpy():
962962
Instruction = dis.Instruction
963963
expected_opinfo_outer = [
964964
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval=(3, 4), argrepr='(3, 4)', offset=0, starts_line=2, is_jump_target=False),
965-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
966-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
965+
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=3, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
966+
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=4, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
967967
Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=6, starts_line=None, is_jump_target=False),
968968
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_f, argrepr=repr(code_object_f), offset=8, starts_line=None, is_jump_target=False),
969969
Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f', argrepr="'outer.<locals>.f'", offset=10, starts_line=None, is_jump_target=False),
970970
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=12, starts_line=None, is_jump_target=False),
971971
Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=14, starts_line=None, is_jump_target=False),
972972
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=16, starts_line=7, is_jump_target=False),
973-
Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='a', argrepr='a', offset=18, starts_line=None, is_jump_target=False),
974-
Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='b', argrepr='b', offset=20, starts_line=None, is_jump_target=False),
973+
Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='a', argrepr='a', offset=18, starts_line=None, is_jump_target=False),
974+
Instruction(opname='LOAD_DEREF', opcode=136, arg=4, argval='b', argrepr='b', offset=20, starts_line=None, is_jump_target=False),
975975
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval='', argrepr="''", offset=22, starts_line=None, is_jump_target=False),
976976
Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval=1, argrepr='1', offset=24, starts_line=None, is_jump_target=False),
977977
Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=26, starts_line=None, is_jump_target=False),
@@ -985,20 +985,20 @@ def jumpy():
985985

986986
expected_opinfo_f = [
987987
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=(5, 6), argrepr='(5, 6)', offset=0, starts_line=3, is_jump_target=False),
988-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=2, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
989-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=3, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
990-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False),
991-
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False),
988+
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=5, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
989+
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=6, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
990+
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=3, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False),
991+
Instruction(opname='LOAD_CLOSURE', opcode=135, arg=4, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False),
992992
Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=10, starts_line=None, is_jump_target=False),
993993
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=12, starts_line=None, is_jump_target=False),
994994
Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f.<locals>.inner', argrepr="'outer.<locals>.f.<locals>.inner'", offset=14, starts_line=None, is_jump_target=False),
995995
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=16, starts_line=None, is_jump_target=False),
996996
Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=18, starts_line=None, is_jump_target=False),
997997
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=20, starts_line=5, is_jump_target=False),
998-
Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='a', argrepr='a', offset=22, starts_line=None, is_jump_target=False),
999-
Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='b', argrepr='b', offset=24, starts_line=None, is_jump_target=False),
1000-
Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='c', argrepr='c', offset=26, starts_line=None, is_jump_target=False),
1001-
Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='d', argrepr='d', offset=28, starts_line=None, is_jump_target=False),
998+
Instruction(opname='LOAD_DEREF', opcode=136, arg=5, argval='a', argrepr='a', offset=22, starts_line=None, is_jump_target=False),
999+
Instruction(opname='LOAD_DEREF', opcode=136, arg=6, argval='b', argrepr='b', offset=24, starts_line=None, is_jump_target=False),
1000+
Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='c', argrepr='c', offset=26, starts_line=None, is_jump_target=False),
1001+
Instruction(opname='LOAD_DEREF', opcode=136, arg=4, argval='d', argrepr='d', offset=28, starts_line=None, is_jump_target=False),
10021002
Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='', offset=30, starts_line=None, is_jump_target=False),
10031003
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=32, starts_line=None, is_jump_target=False),
10041004
Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=34, starts_line=6, is_jump_target=False),
@@ -1007,10 +1007,10 @@ def jumpy():
10071007

10081008
expected_opinfo_inner = [
10091009
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=0, starts_line=4, is_jump_target=False),
1010-
Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
1011-
Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
1012-
Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False),
1013-
Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False),
1010+
Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False),
1011+
Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False),
1012+
Instruction(opname='LOAD_DEREF', opcode=136, arg=4, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False),
1013+
Instruction(opname='LOAD_DEREF', opcode=136, arg=5, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False),
10141014
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='e', argrepr='e', offset=10, starts_line=None, is_jump_target=False),
10151015
Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=12, starts_line=None, is_jump_target=False),
10161016
Instruction(opname='CALL_FUNCTION', opcode=131, arg=6, argval=6, argrepr='', offset=14, starts_line=None, is_jump_target=False),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Compute cell offsets relative to locals in compiler. Allows the interpreter
2+
to treats locals and cells a single array, which is slightly more efficient.
3+
Also make the LOAD_CLOSURE opcode an alias for LOAD_FAST. Preserving
4+
LOAD_CLOSURE helps keep bytecode a bit more readable.

Objects/clinic/codeobject.c.h

+6-16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Objects/codeobject.c

+2-7
Original file line numberDiff line numberDiff line change
@@ -1547,21 +1547,16 @@ code_replace_impl(PyCodeObject *self, int co_argcount,
15471547
code._varname_from_oparg
15481548
15491549
oparg: int
1550-
*
1551-
cell: bool = False
15521550
15531551
(internal-only) Return the local variable name for the given oparg.
15541552
15551553
WARNING: this method is for internal use only and may change or go away.
15561554
[clinic start generated code]*/
15571555

15581556
static PyObject *
1559-
code__varname_from_oparg_impl(PyCodeObject *self, int oparg, int cell)
1560-
/*[clinic end generated code: output=c7d39c9723692c8f input=2945bb291d3a3118]*/
1557+
code__varname_from_oparg_impl(PyCodeObject *self, int oparg)
1558+
/*[clinic end generated code: output=1fd1130413184206 input=c5fa3ee9bac7d4ca]*/
15611559
{
1562-
if (cell) {
1563-
oparg += self->co_nlocals;
1564-
}
15651560
PyObject *name = PyTuple_GetItem(self->co_localsplusnames, oparg);
15661561
if (name == NULL) {
15671562
return NULL;

0 commit comments

Comments
 (0)