Skip to content

Commit 91d9544

Browse files
gh-126835: Make CFG optimizer skip over NOP's when looking for const sequence construction (#129703)
Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
1 parent 8f9c6fa commit 91d9544

File tree

2 files changed

+290
-73
lines changed

2 files changed

+290
-73
lines changed

Lib/test/test_peepholer.py

Lines changed: 202 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ def count_instr_recursively(f, opname):
3636
return count
3737

3838

39+
def get_binop_argval(arg):
40+
for i, nb_op in enumerate(opcode._nb_ops):
41+
if arg == nb_op[0]:
42+
return i
43+
assert False, f"{arg} is not a valid BINARY_OP argument."
44+
45+
3946
class TestTranforms(BytecodeTestCase):
4047

4148
def check_jump_targets(self, code):
@@ -518,8 +525,7 @@ def test_folding_subscript(self):
518525
('("a" * 10)[10]', True),
519526
('(1, (1, 2))[2:6][0][2-1]', True),
520527
]
521-
subscr_argval = 26
522-
assert opcode._nb_ops[subscr_argval][0] == 'NB_SUBSCR'
528+
subscr_argval = get_binop_argval('NB_SUBSCR')
523529
for expr, has_error in tests:
524530
with self.subTest(expr=expr, has_error=has_error):
525531
code = compile(expr, '', 'single')
@@ -1062,6 +1068,200 @@ def test_conditional_jump_forward_non_const_condition(self):
10621068
consts=[0, 1, 2, 3, 4],
10631069
expected_consts=[0, 2, 3])
10641070

1071+
def test_list_exceeding_stack_use_guideline(self):
1072+
def f():
1073+
return [
1074+
0, 1, 2, 3, 4,
1075+
5, 6, 7, 8, 9,
1076+
10, 11, 12, 13, 14,
1077+
15, 16, 17, 18, 19,
1078+
20, 21, 22, 23, 24,
1079+
25, 26, 27, 28, 29,
1080+
30, 31, 32, 33, 34,
1081+
35, 36, 37, 38, 39
1082+
]
1083+
self.assertEqual(f(), list(range(40)))
1084+
1085+
def test_set_exceeding_stack_use_guideline(self):
1086+
def f():
1087+
return {
1088+
0, 1, 2, 3, 4,
1089+
5, 6, 7, 8, 9,
1090+
10, 11, 12, 13, 14,
1091+
15, 16, 17, 18, 19,
1092+
20, 21, 22, 23, 24,
1093+
25, 26, 27, 28, 29,
1094+
30, 31, 32, 33, 34,
1095+
35, 36, 37, 38, 39
1096+
}
1097+
self.assertEqual(f(), frozenset(range(40)))
1098+
1099+
def test_multiple_foldings(self):
1100+
before = [
1101+
('LOAD_SMALL_INT', 1, 0),
1102+
('LOAD_SMALL_INT', 2, 0),
1103+
('BUILD_TUPLE', 1, 0),
1104+
('LOAD_SMALL_INT', 0, 0),
1105+
('BINARY_OP', get_binop_argval('NB_SUBSCR'), 0),
1106+
('BUILD_TUPLE', 2, 0),
1107+
('RETURN_VALUE', None, 0)
1108+
]
1109+
after = [
1110+
('LOAD_CONST', 1, 0),
1111+
('RETURN_VALUE', None, 0)
1112+
]
1113+
self.cfg_optimization_test(before, after, consts=[], expected_consts=[(2,), (1, 2)])
1114+
1115+
def test_build_empty_tuple(self):
1116+
before = [
1117+
('BUILD_TUPLE', 0, 0),
1118+
('RETURN_VALUE', None, 0),
1119+
]
1120+
after = [
1121+
('LOAD_CONST', 0, 0),
1122+
('RETURN_VALUE', None, 0),
1123+
]
1124+
self.cfg_optimization_test(before, after, consts=[], expected_consts=[()])
1125+
1126+
def test_fold_tuple_of_constants(self):
1127+
before = [
1128+
('NOP', None, 0),
1129+
('LOAD_SMALL_INT', 1, 0),
1130+
('NOP', None, 0),
1131+
('LOAD_SMALL_INT', 2, 0),
1132+
('NOP', None, 0),
1133+
('NOP', None, 0),
1134+
('LOAD_SMALL_INT', 3, 0),
1135+
('NOP', None, 0),
1136+
('BUILD_TUPLE', 3, 0),
1137+
('RETURN_VALUE', None, 0),
1138+
]
1139+
after = [
1140+
('LOAD_CONST', 0, 0),
1141+
('RETURN_VALUE', None, 0),
1142+
]
1143+
self.cfg_optimization_test(before, after, consts=[], expected_consts=[(1, 2, 3)])
1144+
1145+
# not enough consts
1146+
same = [
1147+
('LOAD_SMALL_INT', 1, 0),
1148+
('LOAD_SMALL_INT', 2, 0),
1149+
('BUILD_TUPLE', 3, 0),
1150+
('RETURN_VALUE', None, 0)
1151+
]
1152+
self.cfg_optimization_test(same, same, consts=[])
1153+
1154+
# not all consts
1155+
same = [
1156+
('LOAD_SMALL_INT', 1, 0),
1157+
('LOAD_NAME', 0, 0),
1158+
('LOAD_SMALL_INT', 2, 0),
1159+
('BUILD_TUPLE', 3, 0),
1160+
('RETURN_VALUE', None, 0)
1161+
]
1162+
self.cfg_optimization_test(same, same, consts=[])
1163+
1164+
def test_optimize_if_const_list(self):
1165+
before = [
1166+
('NOP', None, 0),
1167+
('LOAD_SMALL_INT', 1, 0),
1168+
('NOP', None, 0),
1169+
('LOAD_SMALL_INT', 2, 0),
1170+
('NOP', None, 0),
1171+
('NOP', None, 0),
1172+
('LOAD_SMALL_INT', 3, 0),
1173+
('NOP', None, 0),
1174+
('BUILD_LIST', 3, 0),
1175+
('RETURN_VALUE', None, 0),
1176+
]
1177+
after = [
1178+
('BUILD_LIST', 0, 0),
1179+
('LOAD_CONST', 0, 0),
1180+
('LIST_EXTEND', 1, 0),
1181+
('RETURN_VALUE', None, 0),
1182+
]
1183+
self.cfg_optimization_test(before, after, consts=[], expected_consts=[(1, 2, 3)])
1184+
1185+
# need minimum 3 consts to optimize
1186+
same = [
1187+
('LOAD_SMALL_INT', 1, 0),
1188+
('LOAD_SMALL_INT', 2, 0),
1189+
('BUILD_LIST', 2, 0),
1190+
('RETURN_VALUE', None, 0),
1191+
]
1192+
self.cfg_optimization_test(same, same, consts=[])
1193+
1194+
# not enough consts
1195+
same = [
1196+
('LOAD_SMALL_INT', 1, 0),
1197+
('LOAD_SMALL_INT', 2, 0),
1198+
('LOAD_SMALL_INT', 3, 0),
1199+
('BUILD_LIST', 4, 0),
1200+
('RETURN_VALUE', None, 0),
1201+
]
1202+
self.cfg_optimization_test(same, same, consts=[])
1203+
1204+
# not all consts
1205+
same = [
1206+
('LOAD_SMALL_INT', 1, 0),
1207+
('LOAD_NAME', 0, 0),
1208+
('LOAD_SMALL_INT', 3, 0),
1209+
('BUILD_LIST', 3, 0),
1210+
('RETURN_VALUE', None, 0),
1211+
]
1212+
self.cfg_optimization_test(same, same, consts=[])
1213+
1214+
def test_optimize_if_const_set(self):
1215+
before = [
1216+
('NOP', None, 0),
1217+
('LOAD_SMALL_INT', 1, 0),
1218+
('NOP', None, 0),
1219+
('LOAD_SMALL_INT', 2, 0),
1220+
('NOP', None, 0),
1221+
('NOP', None, 0),
1222+
('LOAD_SMALL_INT', 3, 0),
1223+
('NOP', None, 0),
1224+
('BUILD_SET', 3, 0),
1225+
('RETURN_VALUE', None, 0),
1226+
]
1227+
after = [
1228+
('BUILD_SET', 0, 0),
1229+
('LOAD_CONST', 0, 0),
1230+
('SET_UPDATE', 1, 0),
1231+
('RETURN_VALUE', None, 0),
1232+
]
1233+
self.cfg_optimization_test(before, after, consts=[], expected_consts=[frozenset({1, 2, 3})])
1234+
1235+
# need minimum 3 consts to optimize
1236+
same = [
1237+
('LOAD_SMALL_INT', 1, 0),
1238+
('LOAD_SMALL_INT', 2, 0),
1239+
('BUILD_SET', 2, 0),
1240+
('RETURN_VALUE', None, 0),
1241+
]
1242+
self.cfg_optimization_test(same, same, consts=[])
1243+
1244+
# not enough consts
1245+
same = [
1246+
('LOAD_SMALL_INT', 1, 0),
1247+
('LOAD_SMALL_INT', 2, 0),
1248+
('LOAD_SMALL_INT', 3, 0),
1249+
('BUILD_SET', 4, 0),
1250+
('RETURN_VALUE', None, 0),
1251+
]
1252+
self.cfg_optimization_test(same, same, consts=[])
1253+
1254+
# not all consts
1255+
same = [
1256+
('LOAD_SMALL_INT', 1, 0),
1257+
('LOAD_NAME', 0, 0),
1258+
('LOAD_SMALL_INT', 3, 0),
1259+
('BUILD_SET', 3, 0),
1260+
('RETURN_VALUE', None, 0),
1261+
]
1262+
self.cfg_optimization_test(same, same, consts=[])
1263+
1264+
10651265
def test_conditional_jump_forward_const_condition(self):
10661266
# The unreachable branch of the jump is removed, the jump
10671267
# becomes redundant and is replaced by a NOP (for the lineno)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy