@@ -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+
3946class 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