@@ -36,6 +36,13 @@ def count_instr_recursively(f, opname):
36
36
return count
37
37
38
38
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
+
39
46
class TestTranforms (BytecodeTestCase ):
40
47
41
48
def check_jump_targets (self , code ):
@@ -518,8 +525,7 @@ def test_folding_subscript(self):
518
525
('("a" * 10)[10]' , True ),
519
526
('(1, (1, 2))[2:6][0][2-1]' , True ),
520
527
]
521
- subscr_argval = 26
522
- assert opcode ._nb_ops [subscr_argval ][0 ] == 'NB_SUBSCR'
528
+ subscr_argval = get_binop_argval ('NB_SUBSCR' )
523
529
for expr , has_error in tests :
524
530
with self .subTest (expr = expr , has_error = has_error ):
525
531
code = compile (expr , '' , 'single' )
@@ -1062,6 +1068,200 @@ def test_conditional_jump_forward_non_const_condition(self):
1062
1068
consts = [0 , 1 , 2 , 3 , 4 ],
1063
1069
expected_consts = [0 , 2 , 3 ])
1064
1070
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 =
8000
span> [
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
+
1065
1265
def test_conditional_jump_forward_const_condition (self ):
1066
1266
# The unreachable branch of the jump is removed, the jump
1067
1267
# becomes redundant and is replaced by a NOP (for the lineno)
0 commit comments