Skip to content

Commit 0689340

Browse files
authored
GH-105229: Replace some superinstructions with single instruction equivalent. (GH-105230)
1 parent e8ecb9e commit 0689340

17 files changed

+731
-686
lines changed

Include/internal/pycore_opcode.h

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

Include/internal/pycore_opcode_utils.h

+2-5
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,8 @@ extern "C" {
5656
(opcode) == RERAISE)
5757

5858
#define IS_SUPERINSTRUCTION_OPCODE(opcode) \
59-
((opcode) == LOAD_FAST__LOAD_FAST || \
60-
(opcode) == LOAD_FAST__LOAD_CONST || \
61-
(opcode) == LOAD_CONST__LOAD_FAST || \
62-
(opcode) == STORE_FAST__LOAD_FAST || \
63-
(opcode) == STORE_FAST__STORE_FAST)
59+
((opcode) == LOAD_FAST__LOAD_CONST || \
60+
(opcode) == LOAD_CONST__LOAD_FAST)
6461

6562

6663
#define LOG_BITS_PER_INT 5

Include/opcode.h

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

Lib/dis.py

+10
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
LOAD_SUPER_ATTR = opmap['LOAD_SUPER_ATTR']
4747
CALL_INTRINSIC_1 = opmap['CALL_INTRINSIC_1']
4848
CALL_INTRINSIC_2 = opmap['CALL_INTRINSIC_2']
49+
LOAD_FAST_LOAD_FAST = opmap['LOAD_FAST_LOAD_FAST']
50+
STORE_FAST_LOAD_FAST = opmap['STORE_FAST_LOAD_FAST']
51+
STORE_FAST_STORE_FAST = opmap['STORE_FAST_STORE_FAST']
4952

5053
CACHE = opmap["CACHE"]
5154

@@ -493,6 +496,13 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
493496
argval = offset + 2 + signed_arg*2
494497
argval += 2 * caches
495498
argrepr = "to " + repr(argval)
499+
elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST):
500+
arg1 = arg >> 4
501+
arg2 = arg & 15
502+
val1, argrepr1 = _get_name_info(arg1, varname_from_oparg)
503+
val2, argrepr2 = _get_name_info(arg2, varname_from_oparg)
504+
argrepr = argrepr1 + ", " + argrepr2
505+
argval = val1, val2
496506
elif deop in haslocal or deop in hasfree:
497507
argval, argrepr = _get_name_info(arg, varname_from_oparg)
498508
elif deop in hascompare:

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ def _write_atomic(path, data, mode=0o666):
446446
# Python 3.12b1 3530 (Shrink the LOAD_SUPER_ATTR caches)
447447
# Python 3.12b1 3531 (Add PEP 695 changes)
448448
# Python 3.13a1 3550 (Plugin optimizer support)
449+
# Python 3.13a1 3551 (Compact superinstructions)
449450

450451
# Python 3.14 will start with 3600
451452

@@ -462,7 +463,7 @@ def _write_atomic(path, data, mode=0o666):
462463
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
463464
# in PC/launcher.c must also be updated.
464465

465-
MAGIC_NUMBER = (3550).to_bytes(2, 'little') + b'\r\n'
466+
MAGIC_NUMBER = (3551).to_bytes(2, 'little') + b'\r\n'
466467

467468
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
468469

Lib/opcode.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ def pseudo_op(name, op, real_ops):
222222
def_op('DICT_MERGE', 164)
223223
def_op('DICT_UPDATE', 165)
224224

225+
def_op('LOAD_FAST_LOAD_FAST', 168)
226+
def_op('STORE_FAST_LOAD_FAST', 169)
227+
def_op('STORE_FAST_STORE_FAST', 170)
225228
def_op('CALL', 171)
226229
def_op('KW_NAMES', 172)
227230
hasconst.append(172)
@@ -411,7 +414,6 @@ def pseudo_op(name, op, real_ops):
411414
],
412415
"LOAD_FAST": [
413416
"LOAD_FAST__LOAD_CONST",
414-
"LOAD_FAST__LOAD_FAST",
415417
],
416418
"LOAD_GLOBAL": [
417419
"LOAD_GLOBAL_BUILTIN",
@@ -422,10 +424,6 @@ def pseudo_op(name, op, real_ops):
422424
"STORE_ATTR_SLOT",
423425
"STORE_ATTR_WITH_HINT",
424426
],
425-
"STORE_FAST": [
426-
"STORE_FAST__LOAD_FAST",
427-
"STORE_FAST__STORE_FAST",
428-
],
429427
"STORE_SUBSCR": [
430428
"STORE_SUBSCR_DICT",
431429
"STORE_SUBSCR_LIST_INT",

Lib/test/test_dis.py

+23-26
Original file line numberDiff line numberDiff line change
@@ -760,15 +760,12 @@ def load_test(x, y=0):
760760
dis_load_test_quickened_code = """\
761761
%3d 0 RESUME 0
762762
763-
%3d 2 LOAD_FAST__LOAD_FAST 0 (x)
764-
4 LOAD_FAST 1 (y)
765-
6 STORE_FAST__STORE_FAST 3 (b)
766-
8 STORE_FAST__LOAD_FAST 2 (a)
767-
768-
%3d 10 LOAD_FAST__LOAD_FAST 2 (a)
769-
12 LOAD_FAST 3 (b)
770-
14 BUILD_TUPLE 2
771-
16 RETURN_VALUE
763+
%3d 2 LOAD_FAST_LOAD_FAST 1 (x, y)
764+
4 STORE_FAST_STORE_FAST 50 (b, a)
765+
766+
%3d 6 LOAD_FAST_LOAD_FAST 35 (a, b)
767+
8 BUILD_TUPLE 2
768+
10 RETURN_VALUE
772769
""" % (load_test.__code__.co_firstlineno,
773770
load_test.__code__.co_firstlineno + 1,
774771
load_test.__code__.co_firstlineno + 2)
@@ -811,9 +808,8 @@ def extended_arg_quick():
811808
%3d 2 LOAD_CONST 1 (Ellipsis)
812809
4 EXTENDED_ARG 1
813810
6 UNPACK_EX 256
814-
8 STORE_FAST 0 (_)
815-
10 STORE_FAST 0 (_)
816-
12 RETURN_CONST 0 (None)
811+
8 STORE_FAST_STORE_FAST 0 (_, _)
812+
10 RETURN_CONST 0 (None)
817813
"""% (extended_arg_quick.__code__.co_firstlineno,
818814
extended_arg_quick.__code__.co_firstlineno + 1,)
819815

@@ -1026,26 +1022,28 @@ def expected(count, w):
10261022
s = ['''\
10271023
1 %*d RESUME 0
10281024
1029-
''' % (w, 0)]
1025+
2 %*d LOAD_FAST 0 (x)
1026+
%*d LOAD_CONST 1 (1)
1027+
%*d BINARY_OP 0 (+)
1028+
''' % (w, 0, w, 2, w, 4, w, 6)]
10301029
s += ['''\
1031-
%*d LOAD_FAST 0 (x)
1030+
%*d STORE_FAST_LOAD_FAST 0 (x, x)
10321031
%*d LOAD_CONST 1 (1)
10331032
%*d BINARY_OP 0 (+)
1034-
%*d STORE_FAST 0 (x)
1035-
''' % (w, 10*i + 2, w, 10*i + 4, w, 10*i + 6, w, 10*i + 10)
1036-
for i in range(count)]
1033+
''' % (w, 8*i + 10, w, 8*i + 12, w, 8*i + 14)
1034+
for i in range(count-1)]
10371035
s += ['''\
1036+
%*d STORE_FAST 0 (x)
10381037
10391038
3 %*d LOAD_FAST 0 (x)
10401039
%*d RETURN_VALUE
1041-
''' % (w, 10*count + 2, w, 10*count + 4)]
1042-
s[1] = ' 2' + s[1][3:]
1040+
''' % (w, 8*count + 2, w, 8*count + 4, w, 8*count + 6)]
10431041
return ''.join(s)
10441042

10451043
for i in range(1, 5):
10461044
self.do_disassembly_test(func(i), expected(i, 4), True)
1047-
self.do_disassembly_test(func(999), expected(999, 4), True)
1048-
self.do_disassembly_test(func(1000), expected(1000, 5), True)
1045+
self.do_disassembly_test(func(1200), expected(1200, 4), True)
1046+
self.do_disassembly_test(func(1300), expected(1300, 5), True)
10491047

10501048
def test_disassemble_str(self):
10511049
self.do_disassembly_test(expr_str, dis_expr_str)
@@ -1646,11 +1644,10 @@ def _prepare_test_cases():
16461644
Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='b', argrepr='b', offset=16, starts_line=None, is_jump_target=False, positions=None),
16471645
Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='c', argrepr='c', offset=18, starts_line=None, is_jump_target=False, positions=None),
16481646
Instruction(opname='LOAD_DEREF', opcode=137, arg=5, argval='d', argrepr='d', offset=20, starts_line=None, is_jump_target=False, positions=None),
1649-
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='e', argrepr='e', offset=22, starts_line=None, is_jump_target=False, positions=None),
1650-
Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=24, starts_line=None, is_jump_target=False, positions=None),
1651-
Instruction(opname='CALL', opcode=171, arg=6, argval=6, argrepr='', offset=26, starts_line=None, is_jump_target=False, positions=None),
1652-
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=34, starts_line=None, is_jump_target=False, positions=None),
1653-
Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=36, starts_line=None, is_jump_target=False, positions=None),
1647+
Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=168, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, starts_line=None, is_jump_target=False, positions=None),
1648+
Instruction(opname='CALL', opcode=171, arg=6, argval=6, argrepr='', offset=24, starts_line=None, is_jump_target=False, positions=None),
1649+
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=32, starts_line=None, is_jump_target=False, positions=None),
1650+
Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=34, starts_line=None, is_jump_target=False, positions=None),
16541651
]
16551652

16561653

Lib/test/test_monitoring.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1158,8 +1158,8 @@ def func():
11581158
('line', 'func', 5),
11591159
('line', 'meth', 1),
11601160
('jump', 'func', 5, 5),
1161-
('jump', 'func', 5, '[offset=114]'),
1162-
('branch', 'func', '[offset=120]', '[offset=122]'),
1161+
('jump', 'func', 5, '[offset=112]'),
1162+
('branch', 'func', '[offset=118]', '[offset=120]'),
11631163
('line', 'check_events', 11)])
11641164

11651165
self.check_events(func, recorders = FLOW_AND_LINE_RECORDERS, expected = [
@@ -1174,8 +1174,8 @@ def func():
11741174
('line', 'meth', 1),
11751175
('return', None),
11761176
('jump', 'func', 5, 5),
1177-
('jump', 'func', 5, '[offset=114]'),
1178-
('branch', 'func', '[offset=120]', '[offset=122]'),
1177+
('jump', 'func', 5, '[offset=112]'),
1178+
('branch', 'func', '[offset=118]', '[offset=120]'),
11791179
('return', None),
11801180
('line', 'check_events', 11)])
11811181

Lib/test/test_peepholer.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ def test_load_fast_known_simple(self):
686686
def f():
687687
x = 1
688688
y = x + x
689-
self.assertInBytecode(f, 'LOAD_FAST')
689+
self.assertInBytecode(f, 'LOAD_FAST_LOAD_FAST')
690690

691691
def test_load_fast_unknown_simple(self):
692692
def f():
@@ -790,7 +790,10 @@ def f():
790790
print(a00, a01, a62, a63)
791791
print(a64, a65, a78, a79)
792792

793-
for i in 0, 1, 62, 63:
793+
self.assertInBytecode(f, 'LOAD_FAST_LOAD_FAST', ("a00", "a01"))
794+
self.assertNotInBytecode(f, 'LOAD_FAST_CHECK', "a00")
795+
self.assertNotInBytecode(f, 'LOAD_FAST_CHECK', "a01")
796+
for i in 62, 63:
794797
# First 64 locals: analyze completely
795798
self.assertInBytecode(f, 'LOAD_FAST', f"a{i:02}")
796799
self.assertNotInBytecode(f, 'LOAD_FAST_CHECK', f"a{i:02}")
@@ -1071,7 +1074,16 @@ def test_no_unsafe_static_swap(self):
10711074
('POP_TOP', 0, 4),
10721075
('RETURN_VALUE', 5)
10731076
]
1074-
self.cfg_optimization_test(insts, insts, consts=list(range(3)), nlocals=1)
1077+
expected_insts = [
1078+
('LOAD_CONST', 0, 1),
1079+
('LOAD_CONST', 1, 2),
1080+
('LOAD_CONST', 2, 3),
1081+
('SWAP', 3, 4),
1082+
('STORE_FAST_STORE_FAST', 17, 4),
1083+
('POP_TOP', 0, 4),
1084+
('RETURN_VALUE', 5)
1085+
]
1086+
self.cfg_optimization_test(insts, expected_insts, consts=list(range(3)), nlocals=1)
10751087

10761088
if __name__ == "__main__":
10771089
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Replace some dynamic superinstructions with single instruction equivalents.

0 commit comments

Comments
 (0)