diff --git a/Doc/library/profiling.sampling.rst b/Doc/library/profiling.sampling.rst index dae67cca66d9b4..9bc58b4d1bc976 100644 --- a/Doc/library/profiling.sampling.rst +++ b/Doc/library/profiling.sampling.rst @@ -1490,6 +1490,13 @@ Output options named ``_.`` (for example, ``flamegraph_12345.html``). :option:`--heatmap` creates a directory named ``heatmap_``. +.. option:: --browser + + Automatically open HTML output (:option:`--flamegraph` and + :option:`--heatmap`) in your default web browser after generation. + When profiling with :option:`--subprocesses`, only the main process + opens the browser; subprocess outputs are never auto-opened. + pstats display options ---------------------- diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 424ec337eb8afd..830b49352e23a3 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1125,7 +1125,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = { [CALL_METHOD_DESCRIPTOR_FAST] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_NOARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [CALL_METHOD_DESCRIPTOR_O] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [CALL_METHOD_DESCRIPTOR_O] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_NON_PY_GENERAL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_PY_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_SYNC_SP_FLAG | HAS_NEEDS_GUARD_IP_FLAG }, [CALL_PY_GENERAL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG | HAS_SYNC_SP_FLAG | HAS_NEEDS_GUARD_IP_FLAG }, @@ -1371,7 +1371,7 @@ _PyOpcode_macro_expansion[256] = { [CALL_METHOD_DESCRIPTOR_FAST] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_METHOD_DESCRIPTOR_NOARGS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_NOARGS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, - [CALL_METHOD_DESCRIPTOR_O] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_O, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, + [CALL_METHOD_DESCRIPTOR_O] = { .nuops = 5, .uops = { { _CALL_METHOD_DESCRIPTOR_O, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_NON_PY_GENERAL] = { .nuops = 3, .uops = { { _CHECK_IS_NOT_PY_CALLABLE, OPARG_SIMPLE, 3 }, { _CALL_NON_PY_GENERAL, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_PY_EXACT_ARGS] = { .nuops = 8, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_FUNCTION_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _CHECK_STACK_SPACE, OPARG_SIMPLE, 3 }, { _CHECK_RECURSION_REMAINING, OPARG_SIMPLE, 3 }, { _INIT_CALL_PY_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } }, [CALL_PY_GENERAL] = { .nuops = 6, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_RECURSION_REMAINING, OPARG_SIMPLE, 3 }, { _PY_FRAME_GENERAL, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } }, @@ -1433,7 +1433,7 @@ _PyOpcode_macro_expansion[256] = { [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_NONDESCRIPTOR_NO_DICT, 4, 5 } } }, [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, OPARG_SIMPLE, 3 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, 4, 5 } } }, [LOAD_ATTR_PROPERTY] = { .nuops = 5, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_PROPERTY_FRAME, 4, 5 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 9 }, { _PUSH_FRAME, OPARG_SIMPLE, 9 } } }, - [LOAD_ATTR_SLOT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, + [LOAD_ATTR_SLOT] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_WITH_HINT] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_WITH_HINT, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_BUILD_CLASS] = { .nuops = 1, .uops = { { _LOAD_BUILD_CLASS, OPARG_SIMPLE, 0 } } }, [LOAD_COMMON_CONSTANT] = { .nuops = 1, .uops = { { _LOAD_COMMON_CONSTANT, OPARG_SIMPLE, 0 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index ebeec6387a741a..e6e6c8266024aa 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -422,7 +422,7 @@ extern "C" { #define _CALL_METHOD_DESCRIPTOR_FAST_r01 616 #define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01 617 #define _CALL_METHOD_DESCRIPTOR_NOARGS_r01 618 -#define _CALL_METHOD_DESCRIPTOR_O_r01 619 +#define _CALL_METHOD_DESCRIPTOR_O_r03 619 #define _CALL_NON_PY_GENERAL_r01 620 #define _CALL_STR_1_r32 621 #define _CALL_TUPLE_1_r32 622 @@ -818,307 +818,309 @@ extern "C" { #define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 1012 #define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 1013 #define _LOAD_ATTR_PROPERTY_FRAME_r11 1014 -#define _LOAD_ATTR_SLOT_r11 1015 -#define _LOAD_ATTR_WITH_HINT_r12 1016 -#define _LOAD_BUILD_CLASS_r01 1017 -#define _LOAD_BYTECODE_r00 1018 -#define _LOAD_COMMON_CONSTANT_r01 1019 -#define _LOAD_COMMON_CONSTANT_r12 1020 -#define _LOAD_COMMON_CONSTANT_r23 1021 -#define _LOAD_CONST_r01 1022 -#define _LOAD_CONST_r12 1023 -#define _LOAD_CONST_r23 1024 -#define _LOAD_CONST_INLINE_r01 1025 -#define _LOAD_CONST_INLINE_r12 1026 -#define _LOAD_CONST_INLINE_r23 1027 -#define _LOAD_CONST_INLINE_BORROW_r01 1028 -#define _LOAD_CONST_INLINE_BORROW_r12 1029 -#define _LOAD_CONST_INLINE_BORROW_r23 1030 -#define _LOAD_CONST_UNDER_INLINE_r02 1031 -#define _LOAD_CONST_UNDER_INLINE_r12 1032 -#define _LOAD_CONST_UNDER_INLINE_r23 1033 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1034 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1035 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1036 -#define _LOAD_DEREF_r01 1037 -#define _LOAD_FAST_r01 1038 -#define _LOAD_FAST_r12 1039 -#define _LOAD_FAST_r23 1040 -#define _LOAD_FAST_0_r01 1041 -#define _LOAD_FAST_0_r12 1042 -#define _LOAD_FAST_0_r23 1043 -#define _LOAD_FAST_1_r01 1044 -#define _LOAD_FAST_1_r12 1045 -#define _LOAD_FAST_1_r23 1046 -#define _LOAD_FAST_2_r01 1047 -#define _LOAD_FAST_2_r12 1048 -#define _LOAD_FAST_2_r23 1049 -#define _LOAD_FAST_3_r01 1050 -#define _LOAD_FAST_3_r12 1051 -#define _LOAD_FAST_3_r23 1052 -#define _LOAD_FAST_4_r01 1053 -#define _LOAD_FAST_4_r12 1054 -#define _LOAD_FAST_4_r23 1055 -#define _LOAD_FAST_5_r01 1056 -#define _LOAD_FAST_5_r12 1057 -#define _LOAD_FAST_5_r23 1058 -#define _LOAD_FAST_6_r01 1059 -#define _LOAD_FAST_6_r12 1060 -#define _LOAD_FAST_6_r23 1061 -#define _LOAD_FAST_7_r01 1062 -#define _LOAD_FAST_7_r12 1063 -#define _LOAD_FAST_7_r23 1064 -#define _LOAD_FAST_AND_CLEAR_r01 1065 -#define _LOAD_FAST_AND_CLEAR_r12 1066 -#define _LOAD_FAST_AND_CLEAR_r23 1067 -#define _LOAD_FAST_BORROW_r01 1068 -#define _LOAD_FAST_BORROW_r12 1069 -#define _LOAD_FAST_BORROW_r23 1070 -#define _LOAD_FAST_BORROW_0_r01 1071 -#define _LOAD_FAST_BORROW_0_r12 1072 -#define _LOAD_FAST_BORROW_0_r23 1073 -#define _LOAD_FAST_BORROW_1_r01 1074 -#define _LOAD_FAST_BORROW_1_r12 1075 -#define _LOAD_FAST_BORROW_1_r23 1076 -#define _LOAD_FAST_BORROW_2_r01 1077 -#define _LOAD_FAST_BORROW_2_r12 1078 -#define _LOAD_FAST_BORROW_2_r23 1079 -#define _LOAD_FAST_BORROW_3_r01 1080 -#define _LOAD_FAST_BORROW_3_r12 1081 -#define _LOAD_FAST_BORROW_3_r23 1082 -#define _LOAD_FAST_BORROW_4_r01 1083 -#define _LOAD_FAST_BORROW_4_r12 1084 -#define _LOAD_FAST_BORROW_4_r23 1085 -#define _LOAD_FAST_BORROW_5_r01 1086 -#define _LOAD_FAST_BORROW_5_r12 1087 -#define _LOAD_FAST_BORROW_5_r23 1088 -#define _LOAD_FAST_BORROW_6_r01 1089 -#define _LOAD_FAST_BORROW_6_r12 1090 -#define _LOAD_FAST_BORROW_6_r23 1091 -#define _LOAD_FAST_BORROW_7_r01 1092 -#define _LOAD_FAST_BORROW_7_r12 1093 -#define _LOAD_FAST_BORROW_7_r23 1094 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1095 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1096 -#define _LOAD_FAST_CHECK_r01 1097 -#define _LOAD_FAST_CHECK_r12 1098 -#define _LOAD_FAST_CHECK_r23 1099 -#define _LOAD_FAST_LOAD_FAST_r02 1100 -#define _LOAD_FAST_LOAD_FAST_r13 1101 -#define _LOAD_FROM_DICT_OR_DEREF_r11 1102 -#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1103 -#define _LOAD_GLOBAL_r00 1104 -#define _LOAD_GLOBAL_BUILTINS_r01 1105 -#define _LOAD_GLOBAL_MODULE_r01 1106 -#define _LOAD_LOCALS_r01 1107 -#define _LOAD_LOCALS_r12 1108 -#define _LOAD_LOCALS_r23 1109 -#define _LOAD_NAME_r01 1110 -#define _LOAD_SMALL_INT_r01 1111 -#define _LOAD_SMALL_INT_r12 1112 -#define _LOAD_SMALL_INT_r23 1113 -#define _LOAD_SMALL_INT_0_r01 1114 -#define _LOAD_SMALL_INT_0_r12 1115 -#define _LOAD_SMALL_INT_0_r23 1116 -#define _LOAD_SMALL_INT_1_r01 1117 -#define _LOAD_SMALL_INT_1_r12 1118 -#define _LOAD_SMALL_INT_1_r23 1119 -#define _LOAD_SMALL_INT_2_r01 1120 -#define _LOAD_SMALL_INT_2_r12 1121 -#define _LOAD_SMALL_INT_2_r23 1122 -#define _LOAD_SMALL_INT_3_r01 1123 -#define _LOAD_SMALL_INT_3_r12 1124 -#define _LOAD_SMALL_INT_3_r23 1125 -#define _LOAD_SPECIAL_r00 1126 -#define _LOAD_SUPER_ATTR_ATTR_r31 1127 -#define _LOAD_SUPER_ATTR_METHOD_r32 1128 -#define _MAKE_CALLARGS_A_TUPLE_r33 1129 -#define _MAKE_CELL_r00 1130 -#define _MAKE_FUNCTION_r11 1131 -#define _MAKE_WARM_r00 1132 -#define _MAKE_WARM_r11 1133 -#define _MAKE_WARM_r22 1134 -#define _MAKE_WARM_r33 1135 -#define _MAP_ADD_r20 1136 -#define _MATCH_CLASS_r31 1137 -#define _MATCH_KEYS_r23 1138 -#define _MATCH_MAPPING_r02 1139 -#define _MATCH_MAPPING_r12 1140 -#define _MATCH_MAPPING_r23 1141 -#define _MATCH_SEQUENCE_r02 1142 -#define _MATCH_SEQUENCE_r12 1143 -#define _MATCH_SEQUENCE_r23 1144 -#define _MAYBE_EXPAND_METHOD_r00 1145 -#define _MAYBE_EXPAND_METHOD_KW_r11 1146 -#define _MONITOR_CALL_r00 1147 -#define _MONITOR_CALL_KW_r11 1148 -#define _MONITOR_JUMP_BACKWARD_r00 1149 -#define _MONITOR_JUMP_BACKWARD_r11 1150 -#define _MONITOR_JUMP_BACKWARD_r22 1151 -#define _MONITOR_JUMP_BACKWARD_r33 1152 -#define _MONITOR_RESUME_r00 1153 -#define _NOP_r00 1154 -#define _NOP_r11 1155 -#define _NOP_r22 1156 -#define _NOP_r33 1157 -#define _POP_CALL_r20 1158 -#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1159 -#define _POP_CALL_ONE_r30 1160 -#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1161 -#define _POP_CALL_TWO_r30 1162 -#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1163 -#define _POP_EXCEPT_r10 1164 -#define _POP_ITER_r20 1165 -#define _POP_JUMP_IF_FALSE_r00 1166 -#define _POP_JUMP_IF_FALSE_r10 1167 -#define _POP_JUMP_IF_FALSE_r21 1168 -#define _POP_JUMP_IF_FALSE_r32 1169 -#define _POP_JUMP_IF_TRUE_r00 1170 -#define _POP_JUMP_IF_TRUE_r10 1171 -#define _POP_JUMP_IF_TRUE_r21 1172 -#define _POP_JUMP_IF_TRUE_r32 1173 -#define _POP_TOP_r10 1174 -#define _POP_TOP_FLOAT_r00 1175 -#define _POP_TOP_FLOAT_r10 1176 -#define _POP_TOP_FLOAT_r21 1177 -#define _POP_TOP_FLOAT_r32 1178 -#define _POP_TOP_INT_r00 1179 -#define _POP_TOP_INT_r10 1180 -#define _POP_TOP_INT_r21 1181 -#define _POP_TOP_INT_r32 1182 -#define _POP_TOP_LOAD_CONST_INLINE_r11 1183 -#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1184 -#define _POP_TOP_NOP_r00 1185 -#define _POP_TOP_NOP_r10 1186 -#define _POP_TOP_NOP_r21 1187 -#define _POP_TOP_NOP_r32 1188 -#define _POP_TOP_UNICODE_r00 1189 -#define _POP_TOP_UNICODE_r10 1190 -#define _POP_TOP_UNICODE_r21 1191 -#define _POP_TOP_UNICODE_r32 1192 -#define _POP_TWO_r20 1193 -#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1194 -#define _PUSH_EXC_INFO_r02 1195 -#define _PUSH_EXC_INFO_r12 1196 -#define _PUSH_EXC_INFO_r23 1197 -#define _PUSH_FRAME_r10 1198 -#define _PUSH_NULL_r01 1199 -#define _PUSH_NULL_r12 1200 -#define _PUSH_NULL_r23 1201 -#define _PUSH_NULL_CONDITIONAL_r00 1202 -#define _PY_FRAME_GENERAL_r01 1203 -#define _PY_FRAME_KW_r11 1204 -#define _QUICKEN_RESUME_r00 1205 -#define _QUICKEN_RESUME_r11 1206 -#define _QUICKEN_RESUME_r22 1207 -#define _QUICKEN_RESUME_r33 1208 -#define _REPLACE_WITH_TRUE_r11 1209 -#define _RESUME_CHECK_r00 1210 -#define _RESUME_CHECK_r11 1211 -#define _RESUME_CHECK_r22 1212 -#define _RESUME_CHECK_r33 1213 -#define _RETURN_GENERATOR_r01 1214 -#define _RETURN_VALUE_r11 1215 -#define _SAVE_RETURN_OFFSET_r00 1216 -#define _SAVE_RETURN_OFFSET_r11 1217 -#define _SAVE_RETURN_OFFSET_r22 1218 -#define _SAVE_RETURN_OFFSET_r33 1219 -#define _SEND_r22 1220 -#define _SEND_GEN_FRAME_r22 1221 -#define _SETUP_ANNOTATIONS_r00 1222 -#define _SET_ADD_r10 1223 -#define _SET_FUNCTION_ATTRIBUTE_r01 1224 -#define _SET_FUNCTION_ATTRIBUTE_r11 1225 -#define _SET_FUNCTION_ATTRIBUTE_r21 1226 -#define _SET_FUNCTION_ATTRIBUTE_r32 1227 -#define _SET_IP_r00 1228 -#define _SET_IP_r11 1229 -#define _SET_IP_r22 1230 -#define _SET_IP_r33 1231 -#define _SET_UPDATE_r10 1232 -#define _SHUFFLE_2_LOAD_CONST_INLINE_BORROW_r02 1233 -#define _SHUFFLE_2_LOAD_CONST_INLINE_BORROW_r12 1234 -#define _SHUFFLE_2_LOAD_CONST_INLINE_BORROW_r22 1235 -#define _SHUFFLE_2_LOAD_CONST_INLINE_BORROW_r32 1236 -#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03 1237 -#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13 1238 -#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23 1239 -#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33 1240 -#define _SPILL_OR_RELOAD_r01 1241 -#define _SPILL_OR_RELOAD_r02 1242 -#define _SPILL_OR_RELOAD_r03 1243 -#define _SPILL_OR_RELOAD_r10 1244 -#define _SPILL_OR_RELOAD_r12 1245 -#define _SPILL_OR_RELOAD_r13 1246 -#define _SPILL_OR_RELOAD_r20 1247 -#define _SPILL_OR_RELOAD_r21 1248 -#define _SPILL_OR_RELOAD_r23 1249 -#define _SPILL_OR_RELOAD_r30 1250 -#define _SPILL_OR_RELOAD_r31 1251 -#define _SPILL_OR_RELOAD_r32 1252 -#define _START_EXECUTOR_r00 1253 -#define _STORE_ATTR_r20 1254 -#define _STORE_ATTR_INSTANCE_VALUE_r21 1255 -#define _STORE_ATTR_SLOT_r21 1256 -#define _STORE_ATTR_WITH_HINT_r21 1257 -#define _STORE_DEREF_r10 1258 -#define _STORE_FAST_r10 1259 -#define _STORE_FAST_0_r10 1260 -#define _STORE_FAST_1_r10 1261 -#define _STORE_FAST_2_r10 1262 -#define _STORE_FAST_3_r10 1263 -#define _STORE_FAST_4_r10 1264 -#define _STORE_FAST_5_r10 1265 -#define _STORE_FAST_6_r10 1266 -#define _STORE_FAST_7_r10 1267 -#define _STORE_FAST_LOAD_FAST_r11 1268 -#define _STORE_FAST_STORE_FAST_r20 1269 -#define _STORE_GLOBAL_r10 1270 -#define _STORE_NAME_r10 1271 -#define _STORE_SLICE_r30 1272 -#define _STORE_SUBSCR_r30 1273 -#define _STORE_SUBSCR_DICT_r31 1274 -#define _STORE_SUBSCR_LIST_INT_r32 1275 -#define _SWAP_r11 1276 -#define _SWAP_2_r02 1277 -#define _SWAP_2_r12 1278 -#define _SWAP_2_r22 1279 -#define _SWAP_2_r33 1280 -#define _SWAP_3_r03 1281 -#define _SWAP_3_r13 1282 -#define _SWAP_3_r23 1283 -#define _SWAP_3_r33 1284 -#define _TIER2_RESUME_CHECK_r00 1285 -#define _TIER2_RESUME_CHECK_r11 1286 -#define _TIER2_RESUME_CHECK_r22 1287 -#define _TIER2_RESUME_CHECK_r33 1288 -#define _TO_BOOL_r11 1289 -#define _TO_BOOL_BOOL_r01 1290 -#define _TO_BOOL_BOOL_r11 1291 -#define _TO_BOOL_BOOL_r22 1292 -#define _TO_BOOL_BOOL_r33 1293 -#define _TO_BOOL_INT_r11 1294 -#define _TO_BOOL_LIST_r11 1295 -#define _TO_BOOL_NONE_r01 1296 -#define _TO_BOOL_NONE_r11 1297 -#define _TO_BOOL_NONE_r22 1298 -#define _TO_BOOL_NONE_r33 1299 -#define _TO_BOOL_STR_r11 1300 -#define _TRACE_RECORD_r00 1301 -#define _UNARY_INVERT_r11 1302 -#define _UNARY_NEGATIVE_r11 1303 -#define _UNARY_NOT_r01 1304 -#define _UNARY_NOT_r11 1305 -#define _UNARY_NOT_r22 1306 -#define _UNARY_NOT_r33 1307 -#define _UNPACK_EX_r10 1308 -#define _UNPACK_SEQUENCE_r10 1309 -#define _UNPACK_SEQUENCE_LIST_r10 1310 -#define _UNPACK_SEQUENCE_TUPLE_r10 1311 -#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1312 -#define _WITH_EXCEPT_START_r33 1313 -#define _YIELD_VALUE_r11 1314 -#define MAX_UOP_REGS_ID 1314 +#define _LOAD_ATTR_SLOT_r02 1015 +#define _LOAD_ATTR_SLOT_r12 1016 +#define _LOAD_ATTR_SLOT_r23 1017 +#define _LOAD_ATTR_WITH_HINT_r12 1018 +#define _LOAD_BUILD_CLASS_r01 1019 +#define _LOAD_BYTECODE_r00 1020 +#define _LOAD_COMMON_CONSTANT_r01 1021 +#define _LOAD_COMMON_CONSTANT_r12 1022 +#define _LOAD_COMMON_CONSTANT_r23 1023 +#define _LOAD_CONST_r01 1024 +#define _LOAD_CONST_r12 1025 +#define _LOAD_CONST_r23 1026 +#define _LOAD_CONST_INLINE_r01 1027 +#define _LOAD_CONST_INLINE_r12 1028 +#define _LOAD_CONST_INLINE_r23 1029 +#define _LOAD_CONST_INLINE_BORROW_r01 1030 +#define _LOAD_CONST_INLINE_BORROW_r12 1031 +#define _LOAD_CONST_INLINE_BORROW_r23 1032 +#define _LOAD_CONST_UNDER_INLINE_r02 1033 +#define _LOAD_CONST_UNDER_INLINE_r12 1034 +#define _LOAD_CONST_UNDER_INLINE_r23 1035 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1036 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1037 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1038 +#define _LOAD_DEREF_r01 1039 +#define _LOAD_FAST_r01 1040 +#define _LOAD_FAST_r12 1041 +#define _LOAD_FAST_r23 1042 +#define _LOAD_FAST_0_r01 1043 +#define _LOAD_FAST_0_r12 1044 +#define _LOAD_FAST_0_r23 1045 +#define _LOAD_FAST_1_r01 1046 +#define _LOAD_FAST_1_r12 1047 +#define _LOAD_FAST_1_r23 1048 +#define _LOAD_FAST_2_r01 1049 +#define _LOAD_FAST_2_r12 1050 +#define _LOAD_FAST_2_r23 1051 +#define _LOAD_FAST_3_r01 1052 +#define _LOAD_FAST_3_r12 1053 +#define _LOAD_FAST_3_r23 1054 +#define _LOAD_FAST_4_r01 1055 +#define _LOAD_FAST_4_r12 1056 +#define _LOAD_FAST_4_r23 1057 +#define _LOAD_FAST_5_r01 1058 +#define _LOAD_FAST_5_r12 1059 +#define _LOAD_FAST_5_r23 1060 +#define _LOAD_FAST_6_r01 1061 +#define _LOAD_FAST_6_r12 1062 +#define _LOAD_FAST_6_r23 1063 +#define _LOAD_FAST_7_r01 1064 +#define _LOAD_FAST_7_r12 1065 +#define _LOAD_FAST_7_r23 1066 +#define _LOAD_FAST_AND_CLEAR_r01 1067 +#define _LOAD_FAST_AND_CLEAR_r12 1068 +#define _LOAD_FAST_AND_CLEAR_r23 1069 +#define _LOAD_FAST_BORROW_r01 1070 +#define _LOAD_FAST_BORROW_r12 1071 +#define _LOAD_FAST_BORROW_r23 1072 +#define _LOAD_FAST_BORROW_0_r01 1073 +#define _LOAD_FAST_BORROW_0_r12 1074 +#define _LOAD_FAST_BORROW_0_r23 1075 +#define _LOAD_FAST_BORROW_1_r01 1076 +#define _LOAD_FAST_BORROW_1_r12 1077 +#define _LOAD_FAST_BORROW_1_r23 1078 +#define _LOAD_FAST_BORROW_2_r01 1079 +#define _LOAD_FAST_BORROW_2_r12 1080 +#define _LOAD_FAST_BORROW_2_r23 1081 +#define _LOAD_FAST_BORROW_3_r01 1082 +#define _LOAD_FAST_BORROW_3_r12 1083 +#define _LOAD_FAST_BORROW_3_r23 1084 +#define _LOAD_FAST_BORROW_4_r01 1085 +#define _LOAD_FAST_BORROW_4_r12 1086 +#define _LOAD_FAST_BORROW_4_r23 1087 +#define _LOAD_FAST_BORROW_5_r01 1088 +#define _LOAD_FAST_BORROW_5_r12 1089 +#define _LOAD_FAST_BORROW_5_r23 1090 +#define _LOAD_FAST_BORROW_6_r01 1091 +#define _LOAD_FAST_BORROW_6_r12 1092 +#define _LOAD_FAST_BORROW_6_r23 1093 +#define _LOAD_FAST_BORROW_7_r01 1094 +#define _LOAD_FAST_BORROW_7_r12 1095 +#define _LOAD_FAST_BORROW_7_r23 1096 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1097 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1098 +#define _LOAD_FAST_CHECK_r01 1099 +#define _LOAD_FAST_CHECK_r12 1100 +#define _LOAD_FAST_CHECK_r23 1101 +#define _LOAD_FAST_LOAD_FAST_r02 1102 +#define _LOAD_FAST_LOAD_FAST_r13 1103 +#define _LOAD_FROM_DICT_OR_DEREF_r11 1104 +#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1105 +#define _LOAD_GLOBAL_r00 1106 +#define _LOAD_GLOBAL_BUILTINS_r01 1107 +#define _LOAD_GLOBAL_MODULE_r01 1108 +#define _LOAD_LOCALS_r01 1109 +#define _LOAD_LOCALS_r12 1110 +#define _LOAD_LOCALS_r23 1111 +#define _LOAD_NAME_r01 1112 +#define _LOAD_SMALL_INT_r01 1113 +#define _LOAD_SMALL_INT_r12 1114 +#define _LOAD_SMALL_INT_r23 1115 +#define _LOAD_SMALL_INT_0_r01 1116 +#define _LOAD_SMALL_INT_0_r12 1117 +#define _LOAD_SMALL_INT_0_r23 1118 +#define _LOAD_SMALL_INT_1_r01 1119 +#define _LOAD_SMALL_INT_1_r12 1120 +#define _LOAD_SMALL_INT_1_r23 1121 +#define _LOAD_SMALL_INT_2_r01 1122 +#define _LOAD_SMALL_INT_2_r12 1123 +#define _LOAD_SMALL_INT_2_r23 1124 +#define _LOAD_SMALL_INT_3_r01 1125 +#define _LOAD_SMALL_INT_3_r12 1126 +#define _LOAD_SMALL_INT_3_r23 1127 +#define _LOAD_SPECIAL_r00 1128 +#define _LOAD_SUPER_ATTR_ATTR_r31 1129 +#define _LOAD_SUPER_ATTR_METHOD_r32 1130 +#define _MAKE_CALLARGS_A_TUPLE_r33 1131 +#define _MAKE_CELL_r00 1132 +#define _MAKE_FUNCTION_r11 1133 +#define _MAKE_WARM_r00 1134 +#define _MAKE_WARM_r11 1135 +#define _MAKE_WARM_r22 1136 +#define _MAKE_WARM_r33 1137 +#define _MAP_ADD_r20 1138 +#define _MATCH_CLASS_r31 1139 +#define _MATCH_KEYS_r23 1140 +#define _MATCH_MAPPING_r02 1141 +#define _MATCH_MAPPING_r12 1142 +#define _MATCH_MAPPING_r23 1143 +#define _MATCH_SEQUENCE_r02 1144 +#define _MATCH_SEQUENCE_r12 1145 +#define _MATCH_SEQUENCE_r23 1146 +#define _MAYBE_EXPAND_METHOD_r00 1147 +#define _MAYBE_EXPAND_METHOD_KW_r11 1148 +#define _MONITOR_CALL_r00 1149 +#define _MONITOR_CALL_KW_r11 1150 +#define _MONITOR_JUMP_BACKWARD_r00 1151 +#define _MONITOR_JUMP_BACKWARD_r11 1152 +#define _MONITOR_JUMP_BACKWARD_r22 1153 +#define _MONITOR_JUMP_BACKWARD_r33 1154 +#define _MONITOR_RESUME_r00 1155 +#define _NOP_r00 1156 +#define _NOP_r11 1157 +#define _NOP_r22 1158 +#define _NOP_r33 1159 +#define _POP_CALL_r20 1160 +#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1161 +#define _POP_CALL_ONE_r30 1162 +#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1163 +#define _POP_CALL_TWO_r30 1164 +#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1165 +#define _POP_EXCEPT_r10 1166 +#define _POP_ITER_r20 1167 +#define _POP_JUMP_IF_FALSE_r00 1168 +#define _POP_JUMP_IF_FALSE_r10 1169 +#define _POP_JUMP_IF_FALSE_r21 1170 +#define _POP_JUMP_IF_FALSE_r32 1171 +#define _POP_JUMP_IF_TRUE_r00 1172 +#define _POP_JUMP_IF_TRUE_r10 1173 +#define _POP_JUMP_IF_TRUE_r21 1174 +#define _POP_JUMP_IF_TRUE_r32 1175 +#define _POP_TOP_r10 1176 +#define _POP_TOP_FLOAT_r00 1177 +#define _POP_TOP_FLOAT_r10 1178 +#define _POP_TOP_FLOAT_r21 1179 +#define _POP_TOP_FLOAT_r32 1180 +#define _POP_TOP_INT_r00 1181 +#define _POP_TOP_INT_r10 1182 +#define _POP_TOP_INT_r21 1183 +#define _POP_TOP_INT_r32 1184 +#define _POP_TOP_LOAD_CONST_INLINE_r11 1185 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1186 +#define _POP_TOP_NOP_r00 1187 +#define _POP_TOP_NOP_r10 1188 +#define _POP_TOP_NOP_r21 1189 +#define _POP_TOP_NOP_r32 1190 +#define _POP_TOP_UNICODE_r00 1191 +#define _POP_TOP_UNICODE_r10 1192 +#define _POP_TOP_UNICODE_r21 1193 +#define _POP_TOP_UNICODE_r32 1194 +#define _POP_TWO_r20 1195 +#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1196 +#define _PUSH_EXC_INFO_r02 1197 +#define _PUSH_EXC_INFO_r12 1198 +#define _PUSH_EXC_INFO_r23 1199 +#define _PUSH_FRAME_r10 1200 +#define _PUSH_NULL_r01 1201 +#define _PUSH_NULL_r12 1202 +#define _PUSH_NULL_r23 1203 +#define _PUSH_NULL_CONDITIONAL_r00 1204 +#define _PY_FRAME_GENERAL_r01 1205 +#define _PY_FRAME_KW_r11 1206 +#define _QUICKEN_RESUME_r00 1207 +#define _QUICKEN_RESUME_r11 1208 +#define _QUICKEN_RESUME_r22 1209 +#define _QUICKEN_RESUME_r33 1210 +#define _REPLACE_WITH_TRUE_r11 1211 +#define _RESUME_CHECK_r00 1212 +#define _RESUME_CHECK_r11 1213 +#define _RESUME_CHECK_r22 1214 +#define _RESUME_CHECK_r33 1215 +#define _RETURN_GENERATOR_r01 1216 +#define _RETURN_VALUE_r11 1217 +#define _SAVE_RETURN_OFFSET_r00 1218 +#define _SAVE_RETURN_OFFSET_r11 1219 +#define _SAVE_RETURN_OFFSET_r22 1220 +#define _SAVE_RETURN_OFFSET_r33 1221 +#define _SEND_r22 1222 +#define _SEND_GEN_FRAME_r22 1223 +#define _SETUP_ANNOTATIONS_r00 1224 +#define _SET_ADD_r10 1225 +#define _SET_FUNCTION_ATTRIBUTE_r01 1226 +#define _SET_FUNCTION_ATTRIBUTE_r11 1227 +#define _SET_FUNCTION_ATTRIBUTE_r21 1228 +#define _SET_FUNCTION_ATTRIBUTE_r32 1229 +#define _SET_IP_r00 1230 +#define _SET_IP_r11 1231 +#define _SET_IP_r22 1232 +#define _SET_IP_r33 1233 +#define _SET_UPDATE_r10 1234 +#define _SHUFFLE_2_LOAD_CONST_INLINE_BORROW_r02 1235 +#define _SHUFFLE_2_LOAD_CONST_INLINE_BORROW_r12 1236 +#define _SHUFFLE_2_LOAD_CONST_INLINE_BORROW_r22 1237 +#define _SHUFFLE_2_LOAD_CONST_INLINE_BORROW_r32 1238 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03 1239 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13 1240 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23 1241 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33 1242 +#define _SPILL_OR_RELOAD_r01 1243 +#define _SPILL_OR_RELOAD_r02 1244 +#define _SPILL_OR_RELOAD_r03 1245 +#define _SPILL_OR_RELOAD_r10 1246 +#define _SPILL_OR_RELOAD_r12 1247 +#define _SPILL_OR_RELOAD_r13 1248 +#define _SPILL_OR_RELOAD_r20 1249 +#define _SPILL_OR_RELOAD_r21 1250 +#define _SPILL_OR_RELOAD_r23 1251 +#define _SPILL_OR_RELOAD_r30 1252 +#define _SPILL_OR_RELOAD_r31 1253 +#define _SPILL_OR_RELOAD_r32 1254 +#define _START_EXECUTOR_r00 1255 +#define _STORE_ATTR_r20 1256 +#define _STORE_ATTR_INSTANCE_VALUE_r21 1257 +#define _STORE_ATTR_SLOT_r21 1258 +#define _STORE_ATTR_WITH_HINT_r21 1259 +#define _STORE_DEREF_r10 1260 +#define _STORE_FAST_r10 1261 +#define _STORE_FAST_0_r10 1262 +#define _STORE_FAST_1_r10 1263 +#define _STORE_FAST_2_r10 1264 +#define _STORE_FAST_3_r10 1265 +#define _STORE_FAST_4_r10 1266 +#define _STORE_FAST_5_r10 1267 +#define _STORE_FAST_6_r10 1268 +#define _STORE_FAST_7_r10 1269 +#define _STORE_FAST_LOAD_FAST_r11 1270 +#define _STORE_FAST_STORE_FAST_r20 1271 +#define _STORE_GLOBAL_r10 1272 +#define _STORE_NAME_r10 1273 +#define _STORE_SLICE_r30 1274 +#define _STORE_SUBSCR_r30 1275 +#define _STORE_SUBSCR_DICT_r31 1276 +#define _STORE_SUBSCR_LIST_INT_r32 1277 +#define _SWAP_r11 1278 +#define _SWAP_2_r02 1279 +#define _SWAP_2_r12 1280 +#define _SWAP_2_r22 1281 +#define _SWAP_2_r33 1282 +#define _SWAP_3_r03 1283 +#define _SWAP_3_r13 1284 +#define _SWAP_3_r23 1285 +#define _SWAP_3_r33 1286 +#define _TIER2_RESUME_CHECK_r00 1287 +#define _TIER2_RESUME_CHECK_r11 1288 +#define _TIER2_RESUME_CHECK_r22 1289 +#define _TIER2_RESUME_CHECK_r33 1290 +#define _TO_BOOL_r11 1291 +#define _TO_BOOL_BOOL_r01 1292 +#define _TO_BOOL_BOOL_r11 1293 +#define _TO_BOOL_BOOL_r22 1294 +#define _TO_BOOL_BOOL_r33 1295 +#define _TO_BOOL_INT_r11 1296 +#define _TO_BOOL_LIST_r11 1297 +#define _TO_BOOL_NONE_r01 1298 +#define _TO_BOOL_NONE_r11 1299 +#define _TO_BOOL_NONE_r22 1300 +#define _TO_BOOL_NONE_r33 1301 +#define _TO_BOOL_STR_r11 1302 +#define _TRACE_RECORD_r00 1303 +#define _UNARY_INVERT_r11 1304 +#define _UNARY_NEGATIVE_r11 1305 +#define _UNARY_NOT_r01 1306 +#define _UNARY_NOT_r11 1307 +#define _UNARY_NOT_r22 1308 +#define _UNARY_NOT_r33 1309 +#define _UNPACK_EX_r10 1310 +#define _UNPACK_SEQUENCE_r10 1311 +#define _UNPACK_SEQUENCE_LIST_r10 1312 +#define _UNPACK_SEQUENCE_TUPLE_r10 1313 +#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1314 +#define _WITH_EXCEPT_START_r33 1315 +#define _YIELD_VALUE_r11 1316 +#define MAX_UOP_REGS_ID 1316 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 1838dd3f0977b2..214f58b22338e1 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -193,7 +193,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_INSTANCE_VALUE] = HAS_DEOPT_FLAG, [_LOAD_ATTR_MODULE] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG, - [_LOAD_ATTR_SLOT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_LOAD_ATTR_SLOT] = HAS_DEOPT_FLAG, [_CHECK_ATTR_CLASS] = HAS_EXIT_FLAG, [_LOAD_ATTR_CLASS] = HAS_ESCAPES_FLAG, [_LOAD_ATTR_PROPERTY_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, @@ -288,7 +288,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CALL_ISINSTANCE] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_GUARD_CALLABLE_LIST_APPEND] = HAS_DEOPT_FLAG, [_CALL_LIST_APPEND] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG, - [_CALL_METHOD_DESCRIPTOR_O] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_CALL_METHOD_DESCRIPTOR_O] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_METHOD_DESCRIPTOR_NOARGS] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_METHOD_DESCRIPTOR_FAST] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -1791,11 +1791,11 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { }, }, [_LOAD_ATTR_SLOT] = { - .best = { 1, 1, 1, 1 }, + .best = { 0, 1, 2, 2 }, .entries = { - { -1, -1, -1 }, - { 1, 1, _LOAD_ATTR_SLOT_r11 }, - { -1, -1, -1 }, + { 2, 0, _LOAD_ATTR_SLOT_r02 }, + { 2, 1, _LOAD_ATTR_SLOT_r12 }, + { 3, 2, _LOAD_ATTR_SLOT_r23 }, { -1, -1, -1 }, }, }, @@ -2648,7 +2648,7 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { [_CALL_METHOD_DESCRIPTOR_O] = { .best = { 0, 0, 0, 0 }, .entries = { - { 1, 0, _CALL_METHOD_DESCRIPTOR_O_r01 }, + { 3, 0, _CALL_METHOD_DESCRIPTOR_O_r03 }, { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 }, @@ -3569,7 +3569,9 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_LOAD_ATTR_INSTANCE_VALUE_r23] = _LOAD_ATTR_INSTANCE_VALUE, [_LOAD_ATTR_MODULE_r11] = _LOAD_ATTR_MODULE, [_LOAD_ATTR_WITH_HINT_r12] = _LOAD_ATTR_WITH_HINT, - [_LOAD_ATTR_SLOT_r11] = _LOAD_ATTR_SLOT, + [_LOAD_ATTR_SLOT_r02] = _LOAD_ATTR_SLOT, + [_LOAD_ATTR_SLOT_r12] = _LOAD_ATTR_SLOT, + [_LOAD_ATTR_SLOT_r23] = _LOAD_ATTR_SLOT, [_CHECK_ATTR_CLASS_r01] = _CHECK_ATTR_CLASS, [_CHECK_ATTR_CLASS_r11] = _CHECK_ATTR_CLASS, [_CHECK_ATTR_CLASS_r22] = _CHECK_ATTR_CLASS, @@ -3764,7 +3766,7 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_CALL_LIST_APPEND_r13] = _CALL_LIST_APPEND, [_CALL_LIST_APPEND_r23] = _CALL_LIST_APPEND, [_CALL_LIST_APPEND_r33] = _CALL_LIST_APPEND, - [_CALL_METHOD_DESCRIPTOR_O_r01] = _CALL_METHOD_DESCRIPTOR_O, + [_CALL_METHOD_DESCRIPTOR_O_r03] = _CALL_METHOD_DESCRIPTOR_O, [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01] = _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, [_CALL_METHOD_DESCRIPTOR_NOARGS_r01] = _CALL_METHOD_DESCRIPTOR_NOARGS, [_CALL_METHOD_DESCRIPTOR_FAST_r01] = _CALL_METHOD_DESCRIPTOR_FAST, @@ -4045,7 +4047,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_CALL_METHOD_DESCRIPTOR_NOARGS] = "_CALL_METHOD_DESCRIPTOR_NOARGS", [_CALL_METHOD_DESCRIPTOR_NOARGS_r01] = "_CALL_METHOD_DESCRIPTOR_NOARGS_r01", [_CALL_METHOD_DESCRIPTOR_O] = "_CALL_METHOD_DESCRIPTOR_O", - [_CALL_METHOD_DESCRIPTOR_O_r01] = "_CALL_METHOD_DESCRIPTOR_O_r01", + [_CALL_METHOD_DESCRIPTOR_O_r03] = "_CALL_METHOD_DESCRIPTOR_O_r03", [_CALL_NON_PY_GENERAL] = "_CALL_NON_PY_GENERAL", [_CALL_NON_PY_GENERAL_r01] = "_CALL_NON_PY_GENERAL_r01", [_CALL_STR_1] = "_CALL_STR_1", @@ -4544,7 +4546,9 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_LOAD_ATTR_PROPERTY_FRAME] = "_LOAD_ATTR_PROPERTY_FRAME", [_LOAD_ATTR_PROPERTY_FRAME_r11] = "_LOAD_ATTR_PROPERTY_FRAME_r11", [_LOAD_ATTR_SLOT] = "_LOAD_ATTR_SLOT", - [_LOAD_ATTR_SLOT_r11] = "_LOAD_ATTR_SLOT_r11", + [_LOAD_ATTR_SLOT_r02] = "_LOAD_ATTR_SLOT_r02", + [_LOAD_ATTR_SLOT_r12] = "_LOAD_ATTR_SLOT_r12", + [_LOAD_ATTR_SLOT_r23] = "_LOAD_ATTR_SLOT_r23", [_LOAD_ATTR_WITH_HINT] = "_LOAD_ATTR_WITH_HINT", [_LOAD_ATTR_WITH_HINT_r12] = "_LOAD_ATTR_WITH_HINT_r12", [_LOAD_BUILD_CLASS] = "_LOAD_BUILD_CLASS", diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 0b7047620b4556..5c4903f14aa86b 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -9,7 +9,7 @@ # types if False: - from typing import IO, Self, ClassVar + from typing import IO, Literal, Self, ClassVar _theme: Theme @@ -74,6 +74,19 @@ class ANSIColors: setattr(NoColors, attr, "") +class CursesColors: + """Curses color constants for terminal UI theming.""" + BLACK = 0 + RED = 1 + GREEN = 2 + YELLOW = 3 + BLUE = 4 + MAGENTA = 5 + CYAN = 6 + WHITE = 7 + DEFAULT = -1 + + # # Experimental theming support (see gh-133346) # @@ -187,6 +200,114 @@ class Difflib(ThemeSection): reset: str = ANSIColors.RESET +@dataclass(frozen=True, kw_only=True) +class LiveProfiler(ThemeSection): + """Theme section for the live profiling TUI (Tachyon profiler). + + Colors use CursesColors constants (BLACK, RED, GREEN, YELLOW, + BLUE, MAGENTA, CYAN, WHITE, DEFAULT). + """ + # Header colors + title_fg: int = CursesColors.CYAN + title_bg: int = CursesColors.DEFAULT + + # Status display colors + pid_fg: int = CursesColors.CYAN + uptime_fg: int = CursesColors.GREEN + time_fg: int = CursesColors.YELLOW + interval_fg: int = CursesColors.MAGENTA + + # Thread view colors + thread_all_fg: int = CursesColors.GREEN + thread_single_fg: int = CursesColors.MAGENTA + + # Progress bar colors + bar_good_fg: int = CursesColors.GREEN + bar_bad_fg: int = CursesColors.RED + + # Stats colors + on_gil_fg: int = CursesColors.GREEN + off_gil_fg: int = CursesColors.RED + waiting_gil_fg: int = CursesColors.YELLOW + gc_fg: int = CursesColors.MAGENTA + + # Function display colors + func_total_fg: int = CursesColors.CYAN + func_exec_fg: int = CursesColors.GREEN + func_stack_fg: int = CursesColors.YELLOW + func_shown_fg: int = CursesColors.MAGENTA + + # Table header colors (for sorted column highlight) + sorted_header_fg: int = CursesColors.BLACK + sorted_header_bg: int = CursesColors.CYAN + + # Normal header colors (non-sorted columns) - use reverse video style + normal_header_fg: int = CursesColors.BLACK + normal_header_bg: int = CursesColors.WHITE + + # Data row colors + samples_fg: int = CursesColors.CYAN + file_fg: int = CursesColors.GREEN + func_fg: int = CursesColors.YELLOW + + # Trend indicator colors + trend_up_fg: int = CursesColors.GREEN + trend_down_fg: int = CursesColors.RED + + # Medal colors for top functions + medal_gold_fg: int = CursesColors.RED + medal_silver_fg: int = CursesColors.YELLOW + medal_bronze_fg: int = CursesColors.GREEN + + # Background style: 'dark' or 'light' + background_style: Literal["dark", "light"] = "dark" + + +LiveProfilerLight = LiveProfiler( + # Header colors + title_fg=CursesColors.BLUE, # Blue is more readable than cyan on light bg + + # Status display colors - darker colors for light backgrounds + pid_fg=CursesColors.BLUE, + uptime_fg=CursesColors.BLACK, + time_fg=CursesColors.BLACK, + interval_fg=CursesColors.BLUE, + + # Thread view colors + thread_all_fg=CursesColors.BLACK, + thread_single_fg=CursesColors.BLUE, + + # Stats colors + waiting_gil_fg=CursesColors.RED, + gc_fg=CursesColors.BLUE, + + # Function display colors + func_total_fg=CursesColors.BLUE, + func_exec_fg=CursesColors.BLACK, + func_stack_fg=CursesColors.BLACK, + func_shown_fg=CursesColors.BLUE, + + # Table header colors (for sorted column highlight) + sorted_header_fg=CursesColors.WHITE, + sorted_header_bg=CursesColors.BLUE, + + # Normal header colors (non-sorted columns) + normal_header_fg=CursesColors.WHITE, + normal_header_bg=CursesColors.BLACK, + + # Data row colors - use dark colors readable on white + samples_fg=CursesColors.BLACK, + file_fg=CursesColors.BLACK, + func_fg=CursesColors.BLUE, # Blue is more readable than magenta on light bg + + # Medal colors for top functions + medal_silver_fg=CursesColors.BLUE, + + # Background style + background_style="light", +) + + @dataclass(frozen=True, kw_only=True) class Syntax(ThemeSection): prompt: str = ANSIColors.BOLD_MAGENTA @@ -232,6 +353,7 @@ class Theme: """ argparse: Argparse = field(default_factory=Argparse) difflib: Difflib = field(default_factory=Difflib) + live_profiler: LiveProfiler = field(default_factory=LiveProfiler) syntax: Syntax = field(default_factory=Syntax) traceback: Traceback = field(default_factory=Traceback) unittest: Unittest = field(default_factory=Unittest) @@ -241,6 +363,7 @@ def copy_with( *, argparse: Argparse | None = None, difflib: Difflib | None = None, + live_profiler: LiveProfiler | None = None, syntax: Syntax | None = None, traceback: Traceback | None = None, unittest: Unittest | None = None, @@ -253,6 +376,7 @@ def copy_with( return type(self)( argparse=argparse or self.argparse, difflib=difflib or self.difflib, + live_profiler=live_profiler or self.live_profiler, syntax=syntax or self.syntax, traceback=traceback or self.traceback, unittest=unittest or self.unittest, @@ -269,6 +393,7 @@ def no_colors(cls) -> Self: return cls( argparse=Argparse.no_colors(), difflib=Difflib.no_colors(), + live_profiler=LiveProfiler.no_colors(), syntax=Syntax.no_colors(), traceback=Traceback.no_colors(), unittest=Unittest.no_colors(), @@ -338,6 +463,9 @@ def _safe_getenv(k: str, fallback: str | None = None) -> str | None: default_theme = Theme() theme_no_color = default_theme.no_colors() +# Convenience theme with light profiler colors (for white/light terminal backgrounds) +light_profiler_theme = default_theme.copy_with(live_profiler=LiveProfilerLight) + def get_theme( *, diff --git a/Lib/profiling/sampling/cli.py b/Lib/profiling/sampling/cli.py index e43925ea8595f0..ea3926c9565809 100644 --- a/Lib/profiling/sampling/cli.py +++ b/Lib/profiling/sampling/cli.py @@ -10,6 +10,7 @@ import subprocess import sys import time +import webbrowser from contextlib import nullcontext from .errors import SamplingUnknownProcessError, SamplingModuleNotFoundError, SamplingScriptNotFoundError @@ -487,6 +488,12 @@ def _add_format_options(parser, include_compression=True, include_binary=True): help="Output path (default: stdout for pstats, auto-generated for others). " "For heatmap: directory name (default: heatmap_PID)", ) + output_group.add_argument( + "--browser", + action="store_true", + help="Automatically open HTML output (flamegraph, heatmap) in browser. " + "When using --subprocesses, only the main process opens the browser", + ) def _add_pstats_options(parser): @@ -586,6 +593,32 @@ def _generate_output_filename(format_type, pid): return f"{format_type}_{pid}.{extension}" +def _open_in_browser(path): + """Open a file or directory in the default web browser. + + Args: + path: File path or directory path to open + + For directories (heatmap), opens the index.html file inside. + """ + abs_path = os.path.abspath(path) + + # For heatmap directories, open the index.html file + if os.path.isdir(abs_path): + index_path = os.path.join(abs_path, 'index.html') + if os.path.exists(index_path): + abs_path = index_path + else: + print(f"Warning: Could not find index.html in {path}", file=sys.stderr) + return + + file_url = f"file://{abs_path}" + try: + webbrowser.open(file_url) + except Exception as e: + print(f"Warning: Could not open browser: {e}", file=sys.stderr) + + def _handle_output(collector, args, pid, mode): """Handle output for the collector based on format and arguments. @@ -625,6 +658,10 @@ def _handle_output(collector, args, pid, mode): filename = args.outfile or _generate_output_filename(args.format, pid) collector.export(filename) + # Auto-open browser for HTML output if --browser flag is set + if args.format in ('flamegraph', 'heatmap') and getattr(args, 'browser', False): + _open_in_browser(filename) + def _validate_args(args, parser): """Validate format-specific options and live mode requirements. @@ -1161,6 +1198,10 @@ def progress_callback(current, total): filename = args.outfile or _generate_output_filename(args.format, os.getpid()) collector.export(filename) + # Auto-open browser for HTML output if --browser flag is set + if args.format in ('flamegraph', 'heatmap') and getattr(args, 'browser', False): + _open_in_browser(filename) + print(f"Replayed {count} samples") diff --git a/Lib/profiling/sampling/heatmap_collector.py b/Lib/profiling/sampling/heatmap_collector.py index bb810fa485be63..022e94d014f9b7 100644 --- a/Lib/profiling/sampling/heatmap_collector.py +++ b/Lib/profiling/sampling/heatmap_collector.py @@ -472,6 +472,10 @@ def __init__(self, *args, **kwargs): self.callers_graph = collections.defaultdict(set) self.function_definitions = {} + # Map each sampled line to its function for proper caller lookup + # (filename, lineno) -> funcname + self.line_to_function = {} + # Edge counting for call path analysis self.edge_samples = collections.Counter() @@ -596,6 +600,10 @@ def _record_line_sample(self, filename, lineno, funcname, is_leaf=False, if funcname and (filename, funcname) not in self.function_definitions: self.function_definitions[(filename, funcname)] = lineno + # Map this line to its function for caller/callee navigation + if funcname: + self.line_to_function[(filename, lineno)] = funcname + def _record_bytecode_sample(self, filename, lineno, opcode, end_lineno=None, col_offset=None, end_col_offset=None, weight=1): @@ -1150,13 +1158,36 @@ def _format_specialization_color(self, spec_pct: int) -> str: return f"rgba({r}, {g}, {b}, {alpha})" def _build_navigation_buttons(self, filename: str, line_num: int) -> str: - """Build navigation buttons for callers/callees.""" + """Build navigation buttons for callers/callees. + + - Callers: All lines in a function show who calls this function + - Callees: Only actual call site lines show what they call + """ line_key = (filename, line_num) - caller_list = self._deduplicate_by_function(self.callers_graph.get(line_key, set())) + + funcname = self.line_to_function.get(line_key) + + # Get callers: look up by function definition line, not current line + # This ensures all lines in a function show who calls this function + if funcname: + func_def_line = self.function_definitions.get((filename, funcname), line_num) + func_def_key = (filename, func_def_line) + caller_list = self._deduplicate_by_function(self.callers_graph.get(func_def_key, set())) + else: + caller_list = self._deduplicate_by_function(self.callers_graph.get(line_key, set())) + + # Get callees: only show for actual call site lines (not every line in function) callee_list = self._deduplicate_by_function(self.call_graph.get(line_key, set())) # Get edge counts for each caller/callee - callers_with_counts = self._get_edge_counts(line_key, caller_list, is_caller=True) + # For callers, use the function definition key for edge lookup + if funcname: + func_def_line = self.function_definitions.get((filename, funcname), line_num) + caller_edge_key = (filename, func_def_line) + else: + caller_edge_key = line_key + callers_with_counts = self._get_edge_counts(caller_edge_key, caller_list, is_caller=True) + # For callees, use the actual line key since that's where the call happens callees_with_counts = self._get_edge_counts(line_key, callee_list, is_caller=False) # Build navigation buttons with counts diff --git a/Lib/profiling/sampling/live_collector/collector.py b/Lib/profiling/sampling/live_collector/collector.py index b31ab060a6b934..c91ed9e0ea9367 100644 --- a/Lib/profiling/sampling/live_collector/collector.py +++ b/Lib/profiling/sampling/live_collector/collector.py @@ -33,6 +33,9 @@ FINISHED_BANNER_EXTRA_LINES, DEFAULT_SORT_BY, DEFAULT_DISPLAY_LIMIT, + COLOR_PAIR_SAMPLES, + COLOR_PAIR_FILE, + COLOR_PAIR_FUNC, COLOR_PAIR_HEADER_BG, COLOR_PAIR_CYAN, COLOR_PAIR_YELLOW, @@ -552,79 +555,61 @@ def _cycle_sort(self, reverse=False): def _setup_colors(self): """Set up color pairs and return color attributes.""" - A_BOLD = self.display.get_attr("A_BOLD") A_REVERSE = self.display.get_attr("A_REVERSE") A_UNDERLINE = self.display.get_attr("A_UNDERLINE") A_NORMAL = self.display.get_attr("A_NORMAL") - # Check both curses color support and _colorize.can_colorize() if self.display.has_colors() and self._can_colorize: with contextlib.suppress(Exception): - # Color constants (using curses values for compatibility) - COLOR_CYAN = 6 - COLOR_GREEN = 2 - COLOR_YELLOW = 3 - COLOR_BLACK = 0 - COLOR_MAGENTA = 5 - COLOR_RED = 1 - - # Initialize all color pairs used throughout the UI - self.display.init_color_pair( - 1, COLOR_CYAN, -1 - ) # Data colors for stats rows - self.display.init_color_pair(2, COLOR_GREEN, -1) - self.display.init_color_pair(3, COLOR_YELLOW, -1) - self.display.init_color_pair( - COLOR_PAIR_HEADER_BG, COLOR_BLACK, COLOR_GREEN - ) - self.display.init_color_pair( - COLOR_PAIR_CYAN, COLOR_CYAN, COLOR_BLACK - ) - self.display.init_color_pair( - COLOR_PAIR_YELLOW, COLOR_YELLOW, COLOR_BLACK - ) - self.display.init_color_pair( - COLOR_PAIR_GREEN, COLOR_GREEN, COLOR_BLACK - ) - self.display.init_color_pair( - COLOR_PAIR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK - ) + theme = _colorize.get_theme(force_color=True).live_profiler + default_bg = -1 + + self.display.init_color_pair(COLOR_PAIR_SAMPLES, theme.samples_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_FILE, theme.file_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_FUNC, theme.func_fg, default_bg) + + # Normal header background color pair self.display.init_color_pair( - COLOR_PAIR_RED, COLOR_RED, COLOR_BLACK + COLOR_PAIR_HEADER_BG, + theme.normal_header_fg, + theme.normal_header_bg, ) + + self.display.init_color_pair(COLOR_PAIR_CYAN, theme.pid_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_YELLOW, theme.time_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_GREEN, theme.uptime_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_MAGENTA, theme.interval_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_RED, theme.off_gil_fg, default_bg) self.display.init_color_pair( - COLOR_PAIR_SORTED_HEADER, COLOR_BLACK, COLOR_YELLOW + COLOR_PAIR_SORTED_HEADER, + theme.sorted_header_fg, + theme.sorted_header_bg, ) + TREND_UP_PAIR = 11 + TREND_DOWN_PAIR = 12 + self.display.init_color_pair(TREND_UP_PAIR, theme.trend_up_fg, default_bg) + self.display.init_color_pair(TREND_DOWN_PAIR, theme.trend_down_fg, default_bg) + return { - "header": self.display.get_color_pair(COLOR_PAIR_HEADER_BG) - | A_BOLD, - "cyan": self.display.get_color_pair(COLOR_PAIR_CYAN) - | A_BOLD, - "yellow": self.display.get_color_pair(COLOR_PAIR_YELLOW) - | A_BOLD, - "green": self.display.get_color_pair(COLOR_PAIR_GREEN) - | A_BOLD, - "magenta": self.display.get_color_pair(COLOR_PAIR_MAGENTA) - | A_BOLD, - "red": self.display.get_color_pair(COLOR_PAIR_RED) - | A_BOLD, - "sorted_header": self.display.get_color_pair( - COLOR_PAIR_SORTED_HEADER - ) - | A_BOLD, - "normal_header": A_REVERSE | A_BOLD, - "color_samples": self.display.get_color_pair(1), - "color_file": self.display.get_color_pair(2), - "color_func": self.display.get_color_pair(3), - # Trend colors (stock-like indicators) - "trend_up": self.display.get_color_pair(COLOR_PAIR_GREEN) | A_BOLD, - "trend_down": self.display.get_color_pair(COLOR_PAIR_RED) | A_BOLD, + "header": self.display.get_color_pair(COLOR_PAIR_HEADER_BG) | A_BOLD, + "cyan": self.display.get_color_pair(COLOR_PAIR_CYAN) | A_BOLD, + "yellow": self.display.get_color_pair(COLOR_PAIR_YELLOW) | A_BOLD, + "green": self.display.get_color_pair(COLOR_PAIR_GREEN) | A_BOLD, + "magenta": self.display.get_color_pair(COLOR_PAIR_MAGENTA) | A_BOLD, + "red": self.display.get_color_pair(COLOR_PAIR_RED) | A_BOLD, + "sorted_header": self.display.get_color_pair(COLOR_PAIR_SORTED_HEADER) | A_BOLD, + "normal_header": self.display.get_color_pair(COLOR_PAIR_HEADER_BG) | A_BOLD, + "color_samples": self.display.get_color_pair(COLOR_PAIR_SAMPLES), + "color_file": self.display.get_color_pair(COLOR_PAIR_FILE), + "color_func": self.display.get_color_pair(COLOR_PAIR_FUNC), + "trend_up": self.display.get_color_pair(TREND_UP_PAIR) | A_BOLD, + "trend_down": self.display.get_color_pair(TREND_DOWN_PAIR) | A_BOLD, "trend_stable": A_NORMAL, } - # Fallback to non-color attributes + # Fallback for no-color mode return { "header": A_REVERSE | A_BOLD, "cyan": A_BOLD, @@ -637,7 +622,6 @@ def _setup_colors(self): "color_samples": A_NORMAL, "color_file": A_NORMAL, "color_func": A_NORMAL, - # Trend colors (fallback to bold/normal for monochrome) "trend_up": A_BOLD, "trend_down": A_BOLD, "trend_stable": A_NORMAL, diff --git a/Lib/profiling/sampling/live_collector/constants.py b/Lib/profiling/sampling/live_collector/constants.py index 4f4575f7b7aae2..bb45006553a67b 100644 --- a/Lib/profiling/sampling/live_collector/constants.py +++ b/Lib/profiling/sampling/live_collector/constants.py @@ -49,6 +49,9 @@ OPCODE_PANEL_HEIGHT = 12 # Height reserved for opcode statistics panel # Color pair IDs +COLOR_PAIR_SAMPLES = 1 +COLOR_PAIR_FILE = 2 +COLOR_PAIR_FUNC = 3 COLOR_PAIR_HEADER_BG = 4 COLOR_PAIR_CYAN = 5 COLOR_PAIR_YELLOW = 6 diff --git a/Lib/profiling/sampling/live_collector/display.py b/Lib/profiling/sampling/live_collector/display.py index d7f65ad73fdc6d..f5324421b10211 100644 --- a/Lib/profiling/sampling/live_collector/display.py +++ b/Lib/profiling/sampling/live_collector/display.py @@ -74,13 +74,17 @@ def get_dimensions(self): return self.stdscr.getmaxyx() def clear(self): - self.stdscr.clear() + # Use erase() instead of clear() to avoid flickering + # clear() forces a complete screen redraw, erase() just clears the buffer + self.stdscr.erase() def refresh(self): self.stdscr.refresh() def redraw(self): - self.stdscr.redrawwin() + # Use noutrefresh + doupdate for smoother updates + self.stdscr.noutrefresh() + curses.doupdate() def add_str(self, line, col, text, attr=0): try: diff --git a/Lib/profiling/sampling/live_collector/widgets.py b/Lib/profiling/sampling/live_collector/widgets.py index cf04f3aa3254ef..ac215dbfeb896e 100644 --- a/Lib/profiling/sampling/live_collector/widgets.py +++ b/Lib/profiling/sampling/live_collector/widgets.py @@ -641,8 +641,6 @@ def render(self, line, width, **kwargs): def draw_column_headers(self, line, width): """Draw column headers with sort indicators.""" - col = 0 - # Determine which columns to show based on width show_sample_pct = width >= WIDTH_THRESHOLD_SAMPLE_PCT show_tottime = width >= WIDTH_THRESHOLD_TOTTIME @@ -661,38 +659,38 @@ def draw_column_headers(self, line, width): "cumtime": 4, }.get(self.collector.sort_by, -1) + # Build the full header line first, then draw it + # This avoids gaps between columns when using reverse video + header_parts = [] + col = 0 + # Column 0: nsamples - attr = sorted_header if sort_col == 0 else normal_header - text = f"{'▼nsamples' if sort_col == 0 else 'nsamples':>13}" - self.add_str(line, col, text, attr) + text = f"{'▼nsamples' if sort_col == 0 else 'nsamples':>13} " + header_parts.append((col, text, sorted_header if sort_col == 0 else normal_header)) col += 15 # Column 1: sample % if show_sample_pct: - attr = sorted_header if sort_col == 1 else normal_header - text = f"{'▼%' if sort_col == 1 else '%':>5}" - self.add_str(line, col, text, attr) + text = f"{'▼%' if sort_col == 1 else '%':>5} " + header_parts.append((col, text, sorted_header if sort_col == 1 else normal_header)) col += 7 # Column 2: tottime if show_tottime: - attr = sorted_header if sort_col == 2 else normal_header - text = f"{'▼tottime' if sort_col == 2 else 'tottime':>10}" - self.add_str(line, col, text, attr) + text = f"{'▼tottime' if sort_col == 2 else 'tottime':>10} " + header_parts.append((col, text, sorted_header if sort_col == 2 else normal_header)) col += 12 # Column 3: cumul % if show_cumul_pct: - attr = sorted_header if sort_col == 3 else normal_header - text = f"{'▼%' if sort_col == 3 else '%':>5}" - self.add_str(line, col, text, attr) + text = f"{'▼%' if sort_col == 3 else '%':>5} " + header_parts.append((col, text, sorted_header if sort_col == 3 else normal_header)) col += 7 # Column 4: cumtime if show_cumtime: - attr = sorted_header if sort_col == 4 else normal_header - text = f"{'▼cumtime' if sort_col == 4 else 'cumtime':>10}" - self.add_str(line, col, text, attr) + text = f"{'▼cumtime' if sort_col == 4 else 'cumtime':>10} " + header_parts.append((col, text, sorted_header if sort_col == 4 else normal_header)) col += 12 # Remaining headers @@ -702,13 +700,22 @@ def draw_column_headers(self, line, width): MAX_FUNC_NAME_WIDTH, max(MIN_FUNC_NAME_WIDTH, remaining_space // 2), ) - self.add_str( - line, col, f"{'function':<{func_width}}", normal_header - ) + text = f"{'function':<{func_width}} " + header_parts.append((col, text, normal_header)) col += func_width + 2 if col < width - 10: - self.add_str(line, col, "file:line", normal_header) + file_text = "file:line" + padding = width - col - len(file_text) + text = file_text + " " * max(0, padding) + header_parts.append((col, text, normal_header)) + + # Draw full-width background first + self.add_str(line, 0, " " * (width - 1), normal_header) + + # Draw each header part on top + for col_pos, text, attr in header_parts: + self.add_str(line, col_pos, text.rstrip(), attr) return ( line + 1, @@ -724,8 +731,7 @@ def draw_stats_rows(self, line, height, width, stats_list, column_flags): column_flags ) - # Get color attributes from the colors dict (already initialized) - color_samples = self.colors.get("color_samples", curses.A_NORMAL) + # Get color attributes color_file = self.colors.get("color_file", curses.A_NORMAL) color_func = self.colors.get("color_func", curses.A_NORMAL) @@ -761,12 +767,12 @@ def draw_stats_rows(self, line, height, width, stats_list, column_flags): # Check if this row is selected is_selected = show_opcodes and row_idx == selected_row - # Helper function to get trend color for a specific column + # Helper function to get trend color def get_trend_color(column_name): if is_selected: return A_REVERSE | A_BOLD trend = trends.get(column_name, "stable") - if trend_tracker is not None: + if trend_tracker is not None and trend_tracker.enabled: return trend_tracker.get_color(trend) return curses.A_NORMAL diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 3780bdb28c8c44..bff97fe8320b22 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2209,8 +2209,28 @@ def testfunc(n): self.assertEqual(res, TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) + pop_tops = [opname for opname in iter_opnames(ex) if opname == "_POP_TOP"] self.assertIn("_CALL_BUILTIN_O", uops) - self.assertIn("_POP_TOP", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(len(pop_tops), 1) + + def test_call_method_descriptor_o(self): + def testfunc(n): + x = 0 + for _ in range(n): + y = (1, 2, 3) + z = y.count(2) + x += z + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + pop_tops = [opname for opname in iter_opnames(ex) if opname == "_POP_TOP"] + self.assertIn("_CALL_METHOD_DESCRIPTOR_O", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(len(pop_tops), 1) def test_get_len_with_const_tuple(self): def testfunc(n): @@ -2573,6 +2593,25 @@ class C: self.assertNotIn("_POP_TOP", uops) self.assertIn("_POP_TOP_NOP", uops) + def test_load_addr_slot(self): + def testfunc(n): + class C: + __slots__ = ('x',) + c = C() + c.x = 42 + x = 0 + for _ in range(n): + x += c.x + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 42 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_LOAD_ATTR_SLOT", uops) + self.assertNotIn("_POP_TOP", uops) + self.assertIn("_POP_TOP_NOP", uops) + def test_int_add_op_refcount_elimination(self): def testfunc(n): c = 1 diff --git a/Lib/test/test_profiling/test_heatmap.py b/Lib/test/test_profiling/test_heatmap.py index b1bfdf868b085a..b2acb1cf577341 100644 --- a/Lib/test/test_profiling/test_heatmap.py +++ b/Lib/test/test_profiling/test_heatmap.py @@ -367,6 +367,96 @@ def test_process_frames_with_file_samples_dict(self): self.assertEqual(collector.file_samples['test.py'][10], 1) +def frame(filename, line, func): + """Create a frame tuple: (filename, location, funcname, opcode).""" + return (filename, (line, line, -1, -1), func, None) + + +class TestHeatmapCollectorNavigationButtons(unittest.TestCase): + """Test navigation button behavior for caller/callee relationships. + + For every call stack: + - Root frames (entry points): only DOWN arrow (callees) + - Middle frames: both UP and DOWN arrows + - Leaf frames: only UP arrow (callers) + """ + + def collect(self, *stacks): + """Create collector and process frame stacks.""" + collector = HeatmapCollector(sample_interval_usec=100) + for stack in stacks: + collector.process_frames(stack, thread_id=1) + return collector + + def test_deep_call_stack_relationships(self): + """Test root/middle/leaf navigation in a 5-level call stack.""" + # Stack: root -> A -> B -> C -> leaf + stack = [ + frame('leaf.py', 5, 'leaf'), + frame('c.py', 10, 'func_c'), + frame('b.py', 15, 'func_b'), + frame('a.py', 20, 'func_a'), + frame('root.py', 25, 'root'), + ] + c = self.collect(stack) + + # Root: only callees (no one calls it) + self.assertIn(('root.py', 25), c.call_graph) + self.assertNotIn(('root.py', 25), c.callers_graph) + + # Middle frames: both callers and callees + for key in [('a.py', 20), ('b.py', 15), ('c.py', 10)]: + self.assertIn(key, c.call_graph) + self.assertIn(key, c.callers_graph) + + # Leaf: only callers (doesn't call anyone) + self.assertNotIn(('leaf.py', 5), c.call_graph) + self.assertIn(('leaf.py', 5), c.callers_graph) + + def test_all_lines_in_function_see_callers(self): + """Test that interior lines map to their function for caller lookup.""" + # Same function sampled at different lines (12, 15, 10) + c = self.collect( + [frame('mod.py', 12, 'my_func'), frame('caller.py', 100, 'caller')], + [frame('mod.py', 15, 'my_func'), frame('caller.py', 100, 'caller')], + [frame('mod.py', 10, 'my_func'), frame('caller.py', 100, 'caller')], + ) + + # All lines should map to same function + for line in [10, 12, 15]: + self.assertEqual(c.line_to_function[('mod.py', line)], 'my_func') + + # Function definition line should have callers + func_def = c.function_definitions[('mod.py', 'my_func')] + self.assertIn(('mod.py', func_def), c.callers_graph) + + def test_multiple_callers_and_callees(self): + """Test multiple callers/callees are recorded correctly.""" + # Two callers -> target, and caller -> two callees + c = self.collect( + [frame('target.py', 10, 'target'), frame('caller1.py', 20, 'c1')], + [frame('target.py', 10, 'target'), frame('caller2.py', 30, 'c2')], + [frame('callee1.py', 5, 'f1'), frame('dispatcher.py', 40, 'dispatch')], + [frame('callee2.py', 6, 'f2'), frame('dispatcher.py', 40, 'dispatch')], + ) + + # Target has 2 callers + callers = c.callers_graph[('target.py', 10)] + self.assertEqual({x[0] for x in callers}, {'caller1.py', 'caller2.py'}) + + # Dispatcher has 2 callees + callees = c.call_graph[('dispatcher.py', 40)] + self.assertEqual({x[0] for x in callees}, {'callee1.py', 'callee2.py'}) + + def test_edge_samples_counted(self): + """Test that repeated calls accumulate edge counts.""" + stack = [frame('callee.py', 10, 'callee'), frame('caller.py', 20, 'caller')] + c = self.collect(stack, stack, stack) + + edge_key = (('caller.py', 20), ('callee.py', 10)) + self.assertEqual(c.edge_samples[edge_key], 3) + + class TestHeatmapCollectorExport(unittest.TestCase): """Test HeatmapCollector.export() method.""" diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_children.py b/Lib/test/test_profiling/test_sampling_profiler/test_children.py index b7dc878a238f8d..84d50cd2088a9e 100644 --- a/Lib/test/test_profiling/test_sampling_profiler/test_children.py +++ b/Lib/test/test_profiling/test_sampling_profiler/test_children.py @@ -438,6 +438,11 @@ def assert_flag_value_pair(flag, value): child_args, f"Flag '--flamegraph' not found in args: {child_args}", ) + self.assertNotIn( + "--browser", + child_args, + f"Flag '--browser' should not be in child args: {child_args}", + ) def test_build_child_profiler_args_no_gc(self): """Test building CLI args with --no-gc.""" diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-01-01-17-01-24.gh-issue-134584.nis8LC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-01-17-01-24.gh-issue-134584.nis8LC.rst new file mode 100644 index 00000000000000..ac5a2b6e8adaff --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-01-17-01-24.gh-issue-134584.nis8LC.rst @@ -0,0 +1 @@ +Eliminate redundant refcounting from ``_LOAD_ATTR_SLOT``. diff --git a/Misc/NEWS.d/next/Library/2025-12-06-19-49-20.gh-issue-138122.m3EF9E.rst b/Misc/NEWS.d/next/Library/2025-12-06-19-49-20.gh-issue-138122.m3EF9E.rst new file mode 100644 index 00000000000000..f4e024828e2a7b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-06-19-49-20.gh-issue-138122.m3EF9E.rst @@ -0,0 +1,5 @@ +The Tachyon profiler's live TUI now integrates with the experimental +:mod:`!_colorize` theming system. Users can customize colors via +:func:`!_colorize.set_theme` (experimental API, subject to change). +A :class:`!LiveProfilerLight` theme is provided for light terminal backgrounds. +Patch by Pablo Galindo. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ea09c0645aa39c..829efafa67d6e1 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2485,7 +2485,7 @@ dummy_func( unused/5 + _PUSH_NULL_CONDITIONAL; - op(_LOAD_ATTR_SLOT, (index/1, owner -- attr)) { + op(_LOAD_ATTR_SLOT, (index/1, owner -- attr, o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **addr = (PyObject **)((char *)owner_o + index); @@ -2498,13 +2498,15 @@ dummy_func( attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - DECREF_INPUTS(); + o = owner; + DEAD(owner); } macro(LOAD_ATTR_SLOT) = unused/1 + _GUARD_TYPE_VERSION + _LOAD_ATTR_SLOT + // NOTE: This action may also deopt + POP_TOP + unused/5 + _PUSH_NULL_CONDITIONAL; @@ -4170,7 +4172,7 @@ dummy_func( _CALL_BUILTIN_CLASS + _CHECK_PERIODIC_AT_END; - op(_CALL_BUILTIN_O, (callable, self_or_null, args[oparg] -- res, a, c)) { + op(_CALL_BUILTIN_O, (callable, self_or_null, args[oparg] -- res, c, s)) { /* Builtin METH_O functions */ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); @@ -4193,8 +4195,8 @@ dummy_func( if (res_o == NULL) { ERROR_NO_POP(); } - a = arg; c = callable; + s = args[0]; INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4362,7 +4364,7 @@ dummy_func( none = PyStackRef_None; } - op(_CALL_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- res)) { + op(_CALL_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- res, c, s, a)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); int total_args = oparg; @@ -4390,8 +4392,13 @@ dummy_func( PyStackRef_AsPyObjectBorrow(arg_stackref)); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } + c = callable; + s = arguments[0]; + a = arguments[1]; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4399,6 +4406,9 @@ dummy_func( unused/1 + unused/2 + _CALL_METHOD_DESCRIPTOR_O + + POP_TOP + + POP_TOP + + POP_TOP + _CHECK_PERIODIC_AT_END; op(_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- res)) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 4e8fa34c0b2c0d..0a2b794988c961 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -8532,11 +8532,49 @@ break; } - case _LOAD_ATTR_SLOT_r11: { + case _LOAD_ATTR_SLOT_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + owner = stack_pointer[-1]; + uint16_t index = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + if (!increfed) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + _tos_cache1 = o; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + break; + } + + case _LOAD_ATTR_SLOT_r12: { CHECK_CURRENT_CACHED_VALUES(1); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef owner; _PyStackRef attr; + _PyStackRef o; _PyStackRef _stack_item_0 = _tos_cache0; owner = _stack_item_0; uint16_t index = (uint16_t)CURRENT_OPERAND0_16(); @@ -8561,21 +8599,52 @@ attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - stack_pointer[0] = owner; - stack_pointer += 1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = owner; - owner = attr; - stack_pointer[-1] = owner; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + o = owner; + _tos_cache1 = o; _tos_cache0 = attr; - _tos_cache1 = PyStackRef_ZERO_BITS; - _tos_cache2 = PyStackRef_ZERO_BITS; - SET_CURRENT_CACHED_VALUES(1); - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(2); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + break; + } + + case _LOAD_ATTR_SLOT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + uint16_t index = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + if (!increfed) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + _tos_cache2 = o; + _tos_cache1 = attr; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } @@ -13568,8 +13637,8 @@ _PyStackRef self_or_null; _PyStackRef callable; _PyStackRef res; - _PyStackRef a; _PyStackRef c; + _PyStackRef s; oparg = CURRENT_OPARG(); args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; @@ -13612,11 +13681,11 @@ SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - a = arg; c = callable; + s = args[0]; res = PyStackRef_FromPyObjectSteal(res_o); - _tos_cache2 = c; - _tos_cache1 = a; + _tos_cache2 = s; + _tos_cache1 = c; _tos_cache0 = res; SET_CURRENT_CACHED_VALUES(3); stack_pointer += -2 - oparg; @@ -14302,13 +14371,16 @@ break; } - case _CALL_METHOD_DESCRIPTOR_O_r01: { + case _CALL_METHOD_DESCRIPTOR_O_r03: { CHECK_CURRENT_CACHED_VALUES(0); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef *args; _PyStackRef self_or_null; _PyStackRef callable; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef a; oparg = CURRENT_OPARG(); args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; @@ -14359,33 +14431,21 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res_o == NULL) { SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } + c = callable; + s = arguments[0]; + a = arguments[1]; res = PyStackRef_FromPyObjectSteal(res_o); - _tos_cache0 = res; - _tos_cache1 = PyStackRef_ZERO_BITS; - _tos_cache2 = PyStackRef_ZERO_BITS; - SET_CURRENT_CACHED_VALUES(1); + _tos_cache2 = a; + _tos_cache1 = s; + _tos_cache0 = c; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e63852aee1134c..716d87fd97ac4a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2327,8 +2327,8 @@ _PyStackRef self_or_null; _PyStackRef *args; _PyStackRef res; - _PyStackRef a; _PyStackRef c; + _PyStackRef s; _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ @@ -2374,15 +2374,15 @@ if (res_o == NULL) { JUMP_TO_LABEL(error); } - a = arg; c = callable; + s = args[0]; res = PyStackRef_FromPyObjectSteal(res_o); } // _POP_TOP { - value = c; + value = s; stack_pointer[-2 - oparg] = res; - stack_pointer[-1 - oparg] = a; + stack_pointer[-1 - oparg] = c; stack_pointer += -oparg; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2391,7 +2391,7 @@ } // _POP_TOP { - value = a; + value = c; stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -3613,6 +3613,10 @@ _PyStackRef self_or_null; _PyStackRef *args; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef a; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ // _CALL_METHOD_DESCRIPTOR_O @@ -3666,34 +3670,46 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res_o == NULL) { JUMP_TO_LABEL(error); } + c = callable; + s = arguments[0]; + a = arguments[1]; res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC_AT_END + // _POP_TOP { - stack_pointer[0] = res; - stack_pointer += 1; + value = a; + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer[-oparg] = s; + stack_pointer += 1 - oparg; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = s; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { _PyFrame_SetStackPointer(frame, stack_pointer); int err = check_periodics(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -8268,6 +8284,8 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; + _PyStackRef o; + _PyStackRef value; _PyStackRef *null; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION @@ -8304,11 +8322,14 @@ attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); + o = owner; + } + // _POP_TOP + { + value = o; + stack_pointer[-1] = attr; _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = owner; - owner = attr; - stack_pointer[-1] = owner; - PyStackRef_CLOSE(tmp); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); } /* Skip 5 cache entries */ diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 3e37cb81388ae0..53c7cb724e1b65 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -131,7 +131,7 @@ dummy_func(void) { } op(_PUSH_NULL, (-- res)) { - res = sym_new_null(ctx); + res = PyJitRef_Borrow(sym_new_null(ctx)); } op(_GUARD_TOS_INT, (value -- value)) { @@ -661,9 +661,10 @@ dummy_func(void) { o = owner; } - op(_LOAD_ATTR_SLOT, (index/1, owner -- attr)) { + op(_LOAD_ATTR_SLOT, (index/1, owner -- attr, o)) { attr = sym_new_not_null(ctx); (void)index; + o = owner; } op(_LOAD_ATTR_CLASS, (descr/4, owner -- attr)) { @@ -1064,6 +1065,35 @@ dummy_func(void) { none = sym_new_const(ctx, Py_None); } + op(_CALL_BUILTIN_O, (callable, self_or_null, args[oparg] -- res, c, s)) { + res = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + } + else if (sym_is_null(self_or_null)) { + s = args[0]; + } + else { + s = sym_new_unknown(ctx); + } + } + + op(_CALL_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- res, c, s, a)) { + res = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + a = args[1]; + } + else { + s = sym_new_unknown(ctx); + a = sym_new_unknown(ctx); + } + } + op(_GUARD_IS_FALSE_POP, (flag -- )) { if (sym_is_const(ctx, flag)) { PyObject *value = sym_get_const(ctx, flag); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index b043a9493cbd7c..49e6ac560306fe 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -185,7 +185,7 @@ case _PUSH_NULL: { JitOptRef res; - res = sym_new_null(ctx); + res = PyJitRef_Borrow(sym_new_null(ctx)); CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; @@ -1666,11 +1666,19 @@ } case _LOAD_ATTR_SLOT: { + JitOptRef owner; JitOptRef attr; + JitOptRef o; + owner = stack_pointer[-1]; uint16_t index = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); (void)index; + o = owner; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; + stack_pointer[0] = o; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2764,16 +2772,31 @@ } case _CALL_BUILTIN_O: { + JitOptRef *args; + JitOptRef self_or_null; + JitOptRef callable; JitOptRef res; - JitOptRef a; JitOptRef c; + JitOptRef s; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; res = sym_new_not_null(ctx); - a = sym_new_not_null(ctx); - c = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + } + else if (sym_is_null(self_or_null)) { + s = args[0]; + } + else { + s = sym_new_unknown(ctx); + } CHECK_STACK_BOUNDS(1 - oparg); stack_pointer[-2 - oparg] = res; - stack_pointer[-1 - oparg] = a; - stack_pointer[-oparg] = c; + stack_pointer[-1 - oparg] = c; + stack_pointer[-oparg] = s; stack_pointer += 1 - oparg; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; @@ -2912,11 +2935,33 @@ } case _CALL_METHOD_DESCRIPTOR_O: { + JitOptRef *args; + JitOptRef self_or_null; + JitOptRef callable; JitOptRef res; + JitOptRef c; + JitOptRef s; + JitOptRef a; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; res = sym_new_not_null(ctx); - CHECK_STACK_BOUNDS(-1 - oparg); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + a = args[1]; + } + else { + s = sym_new_unknown(ctx); + a = sym_new_unknown(ctx); + } + CHECK_STACK_BOUNDS(2 - oparg); stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[-1 - oparg] = c; + stack_pointer[-oparg] = s; + stack_pointer[1 - oparg] = a; + stack_pointer += 2 - oparg; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; }