From 47ec96f13376f7cd53cb6484c5d3ca1f405b52e7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 16 Dec 2025 16:31:52 +0200 Subject: [PATCH 01/80] Post 3.15.0a3 --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 610d5bb4a12..90a73c8f2b1 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -27,7 +27,7 @@ #define PY_RELEASE_SERIAL 3 /* Version as a string */ -#define PY_VERSION "3.15.0a3" +#define PY_VERSION "3.15.0a3+" /*--end constants--*/ From a0434075108efe6acdfba34f42545f4d80ac9a5e Mon Sep 17 00:00:00 2001 From: Edward Xu Date: Tue, 16 Dec 2025 23:04:20 +0800 Subject: [PATCH 02/80] gh-142495: Make `defaultdict` keep existed value when racing with `__missing__` (GH-142668) --- Lib/test/test_defaultdict.py | 18 ++++++++++++++++++ ...5-12-13-23-26-42.gh-issue-142495.I88Uv_.rst | 4 ++++ Modules/_collectionsmodule.c | 10 +++++----- 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-13-23-26-42.gh-issue-142495.I88Uv_.rst diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index bdbe9b81e8f..fbd7354a915 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -186,5 +186,23 @@ class TestDefaultDict(unittest.TestCase): with self.assertRaises(TypeError): i |= None + def test_factory_conflict_with_set_value(self): + key = "conflict_test" + count = 0 + + def default_factory(): + nonlocal count + count += 1 + local_count = count + if count == 1: + test_dict[key] + return local_count + + test_dict = defaultdict(default_factory) + + self.assertEqual(count, 0) + self.assertEqual(test_dict[key], 2) + self.assertEqual(count, 2) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-12-13-23-26-42.gh-issue-142495.I88Uv_.rst b/Misc/NEWS.d/next/Library/2025-12-13-23-26-42.gh-issue-142495.I88Uv_.rst new file mode 100644 index 00000000000..3e1a624fe56 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-13-23-26-42.gh-issue-142495.I88Uv_.rst @@ -0,0 +1,4 @@ +:class:`collections.defaultdict` now prioritizes :meth:`~object.__setitem__` +when inserting default values from ``default_factory``. This prevents race +conditions where a default value would overwrite a value set before +``default_factory`` returns. diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 3ba48d5d9d3..3b14a21fa84 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2231,11 +2231,11 @@ defdict_missing(PyObject *op, PyObject *key) value = _PyObject_CallNoArgs(factory); if (value == NULL) return value; - if (PyObject_SetItem(op, key, value) < 0) { - Py_DECREF(value); - return NULL; - } - return value; + PyObject *result = NULL; + (void)PyDict_SetDefaultRef(op, key, value, &result); + // 'result' is NULL, or a strong reference to 'value' or 'op[key]' + Py_DECREF(value); + return result; } static inline PyObject* From 16a305f15242ed2acac9bfdbb834804a00f6d9c5 Mon Sep 17 00:00:00 2001 From: "Gabriele N. Tornetta" Date: Tue, 16 Dec 2025 16:23:27 +0000 Subject: [PATCH 03/80] Make RESUME monitoring more readable and robust (GH-142136) --- Python/bytecodes.c | 2 +- Python/generated_cases.c.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0dbfe962684..391fa8ea2a9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -240,7 +240,7 @@ dummy_func( op(_MONITOR_RESUME, (--)) { int err = _Py_call_instrumentation( - tstate, oparg > 0, frame, this_instr); + tstate, oparg == 0 ? PY_MONITORING_EVENT_PY_START : PY_MONITORING_EVENT_PY_RESUME, frame, this_instr); ERROR_IF(err); if (frame->instr_ptr != this_instr) { /* Instrumentation has jumped */ diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 2811a2ec8ac..94846d71830 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -7196,7 +7196,7 @@ { _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_call_instrumentation( - tstate, oparg > 0, frame, this_instr); + tstate, oparg == 0 ? PY_MONITORING_EVENT_PY_START : PY_MONITORING_EVENT_PY_RESUME, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { JUMP_TO_LABEL(error); From 92d4aeafd5c678f781526583332a4deb7293f5f8 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 16 Dec 2025 19:57:15 +0000 Subject: [PATCH 04/80] GH-142629: JIT: Fix out of bounds memory read in lltrace (GH-142821) JIT: Fix out of bounds memory read in lltrace --- Python/optimizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 5a2cdb42194..b215e43128f 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -356,7 +356,7 @@ _PyUOpPrint(const _PyUOpInstruction *uop) default: printf(" (%d, Unknown format)", uop->oparg); } - if (_PyUop_Flags[uop->opcode] & HAS_ERROR_FLAG) { + if (_PyUop_Flags[_PyUop_Uncached[uop->opcode]] & HAS_ERROR_FLAG) { printf(", error_target=%d", uop->error_target); } From 6ee51a36b3dfa5789f94256dcd26ddd8af0c3c6d Mon Sep 17 00:00:00 2001 From: Nadeshiko Manju Date: Wed, 17 Dec 2025 05:39:20 +0900 Subject: [PATCH 05/80] gh-134584: Eliminate redundant refcounting from `_LOAD_ATTR_INSTANCE_VALUE` (GH-142769) Signed-off-by: Manjusaka --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_ids.h | 616 +++++++++--------- Include/internal/pycore_uop_metadata.h | 18 +- Lib/test/test_capi/test_opt.py | 19 + ...-12-16-05-52-37.gh-issue-134584.VsfOQR.rst | 1 + Python/bytecodes.c | 6 +- Python/executor_cases.c.h | 100 ++- Python/generated_cases.c.h | 9 +- Python/optimizer_bytecodes.c | 3 +- Python/optimizer_cases.c.h | 8 + 10 files changed, 451 insertions(+), 331 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-05-52-37.gh-issue-134584.VsfOQR.rst diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index ce8a26c551b..c0e3725db1d 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1425,7 +1425,7 @@ _PyOpcode_macro_expansion[256] = { [LOAD_ATTR] = { .nuops = 1, .uops = { { _LOAD_ATTR, OPARG_SIMPLE, 8 } } }, [LOAD_ATTR_CLASS] = { .nuops = 3, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = { .nuops = 4, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _GUARD_TYPE_VERSION, 2, 3 }, { _LOAD_ATTR_CLASS, 4, 5 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, - [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, + [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 5, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } }, [LOAD_ATTR_METHOD_LAZY_DICT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_METHOD_LAZY_DICT, 1, 3 }, { _LOAD_ATTR_METHOD_LAZY_DICT, 4, 5 } } }, [LOAD_ATTR_METHOD_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_METHOD_NO_DICT, 4, 5 } } }, [LOAD_ATTR_METHOD_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_METHOD_WITH_VALUES, 4, 5 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index df623f49b0d..5519e14e902 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -788,313 +788,315 @@ extern "C" { #define _LOAD_ATTR_r10 981 #define _LOAD_ATTR_CLASS_r11 982 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_r11 983 -#define _LOAD_ATTR_INSTANCE_VALUE_r11 984 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 985 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 986 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 987 -#define _LOAD_ATTR_METHOD_NO_DICT_r02 988 -#define _LOAD_ATTR_METHOD_NO_DICT_r12 989 -#define _LOAD_ATTR_METHOD_NO_DICT_r23 990 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 991 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 992 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 993 -#define _LOAD_ATTR_MODULE_r11 994 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 995 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 996 -#define _LOAD_ATTR_PROPERTY_FRAME_r11 997 -#define _LOAD_ATTR_SLOT_r11 998 -#define _LOAD_ATTR_WITH_HINT_r11 999 -#define _LOAD_BUILD_CLASS_r01 1000 -#define _LOAD_BYTECODE_r00 1001 -#define _LOAD_COMMON_CONSTANT_r01 1002 -#define _LOAD_COMMON_CONSTANT_r12 1003 -#define _LOAD_COMMON_CONSTANT_r23 1004 -#define _LOAD_CONST_r01 1005 -#define _LOAD_CONST_r12 1006 -#define _LOAD_CONST_r23 1007 -#define _LOAD_CONST_INLINE_r01 1008 -#define _LOAD_CONST_INLINE_r12 1009 -#define _LOAD_CONST_INLINE_r23 1010 -#define _LOAD_CONST_INLINE_BORROW_r01 1011 -#define _LOAD_CONST_INLINE_BORROW_r12 1012 -#define _LOAD_CONST_INLINE_BORROW_r23 1013 -#define _LOAD_CONST_UNDER_INLINE_r02 1014 -#define _LOAD_CONST_UNDER_INLINE_r12 1015 -#define _LOAD_CONST_UNDER_INLINE_r23 1016 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1017 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1018 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1019 -#define _LOAD_DEREF_r01 1020 -#define _LOAD_FAST_r01 1021 -#define _LOAD_FAST_r12 1022 -#define _LOAD_FAST_r23 1023 -#define _LOAD_FAST_0_r01 1024 -#define _LOAD_FAST_0_r12 1025 -#define _LOAD_FAST_0_r23 1026 -#define _LOAD_FAST_1_r01 1027 -#define _LOAD_FAST_1_r12 1028 -#define _LOAD_FAST_1_r23 1029 -#define _LOAD_FAST_2_r01 1030 -#define _LOAD_FAST_2_r12 1031 -#define _LOAD_FAST_2_r23 1032 -#define _LOAD_FAST_3_r01 1033 -#define _LOAD_FAST_3_r12 1034 -#define _LOAD_FAST_3_r23 1035 -#define _LOAD_FAST_4_r01 1036 -#define _LOAD_FAST_4_r12 1037 -#define _LOAD_FAST_4_r23 1038 -#define _LOAD_FAST_5_r01 1039 -#define _LOAD_FAST_5_r12 1040 -#define _LOAD_FAST_5_r23 1041 -#define _LOAD_FAST_6_r01 1042 -#define _LOAD_FAST_6_r12 1043 -#define _LOAD_FAST_6_r23 1044 -#define _LOAD_FAST_7_r01 1045 -#define _LOAD_FAST_7_r12 1046 -#define _LOAD_FAST_7_r23 1047 -#define _LOAD_FAST_AND_CLEAR_r01 1048 -#define _LOAD_FAST_AND_CLEAR_r12 1049 -#define _LOAD_FAST_AND_CLEAR_r23 1050 -#define _LOAD_FAST_BORROW_r01 1051 -#define _LOAD_FAST_BORROW_r12 1052 -#define _LOAD_FAST_BORROW_r23 1053 -#define _LOAD_FAST_BORROW_0_r01 1054 -#define _LOAD_FAST_BORROW_0_r12 1055 -#define _LOAD_FAST_BORROW_0_r23 1056 -#define _LOAD_FAST_BORROW_1_r01 1057 -#define _LOAD_FAST_BORROW_1_r12 1058 -#define _LOAD_FAST_BORROW_1_r23 1059 -#define _LOAD_FAST_BORROW_2_r01 1060 -#define _LOAD_FAST_BORROW_2_r12 1061 -#define _LOAD_FAST_BORROW_2_r23 1062 -#define _LOAD_FAST_BORROW_3_r01 1063 -#define _LOAD_FAST_BORROW_3_r12 1064 -#define _LOAD_FAST_BORROW_3_r23 1065 -#define _LOAD_FAST_BORROW_4_r01 1066 -#define _LOAD_FAST_BORROW_4_r12 1067 -#define _LOAD_FAST_BORROW_4_r23 1068 -#define _LOAD_FAST_BORROW_5_r01 1069 -#define _LOAD_FAST_BORROW_5_r12 1070 -#define _LOAD_FAST_BORROW_5_r23 1071 -#define _LOAD_FAST_BORROW_6_r01 1072 -#define _LOAD_FAST_BORROW_6_r12 1073 -#define _LOAD_FAST_BORROW_6_r23 1074 -#define _LOAD_FAST_BORROW_7_r01 1075 -#define _LOAD_FAST_BORROW_7_r12 1076 -#define _LOAD_FAST_BORROW_7_r23 1077 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1078 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1079 -#define _LOAD_FAST_CHECK_r01 1080 -#define _LOAD_FAST_CHECK_r12 1081 -#define _LOAD_FAST_CHECK_r23 1082 -#define _LOAD_FAST_LOAD_FAST_r02 1083 -#define _LOAD_FAST_LOAD_FAST_r13 1084 -#define _LOAD_FROM_DICT_OR_DEREF_r11 1085 -#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1086 -#define _LOAD_GLOBAL_r00 1087 -#define _LOAD_GLOBAL_BUILTINS_r01 1088 -#define _LOAD_GLOBAL_MODULE_r01 1089 -#define _LOAD_LOCALS_r01 1090 -#define _LOAD_LOCALS_r12 1091 -#define _LOAD_LOCALS_r23 1092 -#define _LOAD_NAME_r01 1093 -#define _LOAD_SMALL_INT_r01 1094 -#define _LOAD_SMALL_INT_r12 1095 -#define _LOAD_SMALL_INT_r23 1096 -#define _LOAD_SMALL_INT_0_r01 1097 -#define _LOAD_SMALL_INT_0_r12 1098 -#define _LOAD_SMALL_INT_0_r23 1099 -#define _LOAD_SMALL_INT_1_r01 1100 -#define _LOAD_SMALL_INT_1_r12 1101 -#define _LOAD_SMALL_INT_1_r23 1102 -#define _LOAD_SMALL_INT_2_r01 1103 -#define _LOAD_SMALL_INT_2_r12 1104 -#define _LOAD_SMALL_INT_2_r23 1105 -#define _LOAD_SMALL_INT_3_r01 1106 -#define _LOAD_SMALL_INT_3_r12 1107 -#define _LOAD_SMALL_INT_3_r23 1108 -#define _LOAD_SPECIAL_r00 1109 -#define _LOAD_SUPER_ATTR_ATTR_r31 1110 -#define _LOAD_SUPER_ATTR_METHOD_r32 1111 -#define _MAKE_CALLARGS_A_TUPLE_r33 1112 -#define _MAKE_CELL_r00 1113 -#define _MAKE_FUNCTION_r11 1114 -#define _MAKE_WARM_r00 1115 -#define _MAKE_WARM_r11 1116 -#define _MAKE_WARM_r22 1117 -#define _MAKE_WARM_r33 1118 -#define _MAP_ADD_r20 1119 -#define _MATCH_CLASS_r31 1120 -#define _MATCH_KEYS_r23 1121 -#define _MATCH_MAPPING_r02 1122 -#define _MATCH_MAPPING_r12 1123 -#define _MATCH_MAPPING_r23 1124 -#define _MATCH_SEQUENCE_r02 1125 -#define _MATCH_SEQUENCE_r12 1126 -#define _MATCH_SEQUENCE_r23 1127 -#define _MAYBE_EXPAND_METHOD_r00 1128 -#define _MAYBE_EXPAND_METHOD_KW_r11 1129 -#define _MONITOR_CALL_r00 1130 -#define _MONITOR_CALL_KW_r11 1131 -#define _MONITOR_JUMP_BACKWARD_r00 1132 -#define _MONITOR_JUMP_BACKWARD_r11 1133 -#define _MONITOR_JUMP_BACKWARD_r22 1134 -#define _MONITOR_JUMP_BACKWARD_r33 1135 -#define _MONITOR_RESUME_r00 1136 -#define _NOP_r00 1137 -#define _NOP_r11 1138 -#define _NOP_r22 1139 -#define _NOP_r33 1140 -#define _POP_CALL_r20 1141 -#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1142 -#define _POP_CALL_ONE_r30 1143 -#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1144 -#define _POP_CALL_TWO_r30 1145 -#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1146 -#define _POP_EXCEPT_r10 1147 -#define _POP_ITER_r20 1148 -#define _POP_JUMP_IF_FALSE_r00 1149 -#define _POP_JUMP_IF_FALSE_r10 1150 -#define _POP_JUMP_IF_FALSE_r21 1151 -#define _POP_JUMP_IF_FALSE_r32 1152 -#define _POP_JUMP_IF_TRUE_r00 1153 -#define _POP_JUMP_IF_TRUE_r10 1154 -#define _POP_JUMP_IF_TRUE_r21 1155 -#define _POP_JUMP_IF_TRUE_r32 1156 -#define _POP_TOP_r10 1157 -#define _POP_TOP_FLOAT_r00 1158 -#define _POP_TOP_FLOAT_r10 1159 -#define _POP_TOP_FLOAT_r21 1160 -#define _POP_TOP_FLOAT_r32 1161 -#define _POP_TOP_INT_r00 1162 -#define _POP_TOP_INT_r10 1163 -#define _POP_TOP_INT_r21 1164 -#define _POP_TOP_INT_r32 1165 -#define _POP_TOP_LOAD_CONST_INLINE_r11 1166 -#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1167 -#define _POP_TOP_NOP_r00 1168 -#define _POP_TOP_NOP_r10 1169 -#define _POP_TOP_NOP_r21 1170 -#define _POP_TOP_NOP_r32 1171 -#define _POP_TOP_UNICODE_r00 1172 -#define _POP_TOP_UNICODE_r10 1173 -#define _POP_TOP_UNICODE_r21 1174 -#define _POP_TOP_UNICODE_r32 1175 -#define _POP_TWO_r20 1176 -#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1177 -#define _PUSH_EXC_INFO_r02 1178 -#define _PUSH_EXC_INFO_r12 1179 -#define _PUSH_EXC_INFO_r23 1180 -#define _PUSH_FRAME_r10 1181 -#define _PUSH_NULL_r01 1182 -#define _PUSH_NULL_r12 1183 -#define _PUSH_NULL_r23 1184 -#define _PUSH_NULL_CONDITIONAL_r00 1185 -#define _PY_FRAME_GENERAL_r01 1186 -#define _PY_FRAME_KW_r11 1187 -#define _QUICKEN_RESUME_r00 1188 -#define _QUICKEN_RESUME_r11 1189 -#define _QUICKEN_RESUME_r22 1190 -#define _QUICKEN_RESUME_r33 1191 -#define _REPLACE_WITH_TRUE_r11 1192 -#define _RESUME_CHECK_r00 1193 -#define _RESUME_CHECK_r11 1194 -#define _RESUME_CHECK_r22 1195 -#define _RESUME_CHECK_r33 1196 -#define _RETURN_GENERATOR_r01 1197 -#define _RETURN_VALUE_r11 1198 -#define _SAVE_RETURN_OFFSET_r00 1199 -#define _SAVE_RETURN_OFFSET_r11 1200 -#define _SAVE_RETURN_OFFSET_r22 1201 -#define _SAVE_RETURN_OFFSET_r33 1202 -#define _SEND_r22 1203 -#define _SEND_GEN_FRAME_r22 1204 -#define _SETUP_ANNOTATIONS_r00 1205 -#define _SET_ADD_r10 1206 -#define _SET_FUNCTION_ATTRIBUTE_r01 1207 -#define _SET_FUNCTION_ATTRIBUTE_r11 1208 -#define _SET_FUNCTION_ATTRIBUTE_r21 1209 -#define _SET_FUNCTION_ATTRIBUTE_r32 1210 -#define _SET_IP_r00 1211 -#define _SET_IP_r11 1212 -#define _SET_IP_r22 1213 -#define _SET_IP_r33 1214 -#define _SET_UPDATE_r10 1215 -#define _SPILL_OR_RELOAD_r01 1216 -#define _SPILL_OR_RELOAD_r02 1217 -#define _SPILL_OR_RELOAD_r03 1218 -#define _SPILL_OR_RELOAD_r10 1219 -#define _SPILL_OR_RELOAD_r12 1220 -#define _SPILL_OR_RELOAD_r13 1221 -#define _SPILL_OR_RELOAD_r20 1222 -#define _SPILL_OR_RELOAD_r21 1223 -#define _SPILL_OR_RELOAD_r23 1224 -#define _SPILL_OR_RELOAD_r30 1225 -#define _SPILL_OR_RELOAD_r31 1226 -#define _SPILL_OR_RELOAD_r32 1227 -#define _START_EXECUTOR_r00 1228 -#define _STORE_ATTR_r20 1229 -#define _STORE_ATTR_INSTANCE_VALUE_r21 1230 -#define _STORE_ATTR_SLOT_r21 1231 -#define _STORE_ATTR_WITH_HINT_r20 1232 -#define _STORE_DEREF_r10 1233 -#define _STORE_FAST_r10 1234 -#define _STORE_FAST_0_r10 1235 -#define _STORE_FAST_1_r10 1236 -#define _STORE_FAST_2_r10 1237 -#define _STORE_FAST_3_r10 1238 -#define _STORE_FAST_4_r10 1239 -#define _STORE_FAST_5_r10 1240 -#define _STORE_FAST_6_r10 1241 -#define _STORE_FAST_7_r10 1242 -#define _STORE_FAST_LOAD_FAST_r11 1243 -#define _STORE_FAST_STORE_FAST_r20 1244 -#define _STORE_GLOBAL_r10 1245 -#define _STORE_NAME_r10 1246 -#define _STORE_SLICE_r30 1247 -#define _STORE_SUBSCR_r30 1248 -#define _STORE_SUBSCR_DICT_r31 1249 -#define _STORE_SUBSCR_LIST_INT_r32 1250 -#define _SWAP_r11 1251 -#define _SWAP_2_r02 1252 -#define _SWAP_2_r12 1253 -#define _SWAP_2_r22 1254 -#define _SWAP_2_r33 1255 -#define _SWAP_3_r03 1256 -#define _SWAP_3_r13 1257 -#define _SWAP_3_r23 1258 -#define _SWAP_3_r33 1259 -#define _TIER2_RESUME_CHECK_r00 1260 -#define _TIER2_RESUME_CHECK_r11 1261 -#define _TIER2_RESUME_CHECK_r22 1262 -#define _TIER2_RESUME_CHECK_r33 1263 -#define _TO_BOOL_r11 1264 -#define _TO_BOOL_BOOL_r01 1265 -#define _TO_BOOL_BOOL_r11 1266 -#define _TO_BOOL_BOOL_r22 1267 -#define _TO_BOOL_BOOL_r33 1268 -#define _TO_BOOL_INT_r11 1269 -#define _TO_BOOL_LIST_r11 1270 -#define _TO_BOOL_NONE_r01 1271 -#define _TO_BOOL_NONE_r11 1272 -#define _TO_BOOL_NONE_r22 1273 -#define _TO_BOOL_NONE_r33 1274 -#define _TO_BOOL_STR_r11 1275 -#define _TRACE_RECORD_r00 1276 -#define _UNARY_INVERT_r11 1277 -#define _UNARY_NEGATIVE_r11 1278 -#define _UNARY_NOT_r01 1279 -#define _UNARY_NOT_r11 1280 -#define _UNARY_NOT_r22 1281 -#define _UNARY_NOT_r33 1282 -#define _UNPACK_EX_r10 1283 -#define _UNPACK_SEQUENCE_r10 1284 -#define _UNPACK_SEQUENCE_LIST_r10 1285 -#define _UNPACK_SEQUENCE_TUPLE_r10 1286 -#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1287 -#define _WITH_EXCEPT_START_r33 1288 -#define _YIELD_VALUE_r11 1289 -#define MAX_UOP_REGS_ID 1289 +#define _LOAD_ATTR_INSTANCE_VALUE_r02 984 +#define _LOAD_ATTR_INSTANCE_VALUE_r12 985 +#define _LOAD_ATTR_INSTANCE_VALUE_r23 986 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 987 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 988 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 989 +#define _LOAD_ATTR_METHOD_NO_DICT_r02 990 +#define _LOAD_ATTR_METHOD_NO_DICT_r12 991 +#define _LOAD_ATTR_METHOD_NO_DICT_r23 992 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 993 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 994 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 995 +#define _LOAD_ATTR_MODULE_r11 996 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 997 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 998 +#define _LOAD_ATTR_PROPERTY_FRAME_r11 999 +#define _LOAD_ATTR_SLOT_r11 1000 +#define _LOAD_ATTR_WITH_HINT_r11 1001 +#define _LOAD_BUILD_CLASS_r01 1002 +#define _LOAD_BYTECODE_r00 1003 +#define _LOAD_COMMON_CONSTANT_r01 1004 +#define _LOAD_COMMON_CONSTANT_r12 1005 +#define _LOAD_COMMON_CONSTANT_r23 1006 +#define _LOAD_CONST_r01 1007 +#define _LOAD_CONST_r12 1008 +#define _LOAD_CONST_r23 1009 +#define _LOAD_CONST_INLINE_r01 1010 +#define _LOAD_CONST_INLINE_r12 1011 +#define _LOAD_CONST_INLINE_r23 1012 +#define _LOAD_CONST_INLINE_BORROW_r01 1013 +#define _LOAD_CONST_INLINE_BORROW_r12 1014 +#define _LOAD_CONST_INLINE_BORROW_r23 1015 +#define _LOAD_CONST_UNDER_INLINE_r02 1016 +#define _LOAD_CONST_UNDER_INLINE_r12 1017 +#define _LOAD_CONST_UNDER_INLINE_r23 1018 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1019 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1020 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1021 +#define _LOAD_DEREF_r01 1022 +#define _LOAD_FAST_r01 1023 +#define _LOAD_FAST_r12 1024 +#define _LOAD_FAST_r23 1025 +#define _LOAD_FAST_0_r01 1026 +#define _LOAD_FAST_0_r12 1027 +#define _LOAD_FAST_0_r23 1028 +#define _LOAD_FAST_1_r01 1029 +#define _LOAD_FAST_1_r12 1030 +#define _LOAD_FAST_1_r23 1031 +#define _LOAD_FAST_2_r01 1032 +#define _LOAD_FAST_2_r12 1033 +#define _LOAD_FAST_2_r23 1034 +#define _LOAD_FAST_3_r01 1035 +#define _LOAD_FAST_3_r12 1036 +#define _LOAD_FAST_3_r23 1037 +#define _LOAD_FAST_4_r01 1038 +#define _LOAD_FAST_4_r12 1039 +#define _LOAD_FAST_4_r23 1040 +#define _LOAD_FAST_5_r01 1041 +#define _LOAD_FAST_5_r12 1042 +#define _LOAD_FAST_5_r23 1043 +#define _LOAD_FAST_6_r01 1044 +#define _LOAD_FAST_6_r12 1045 +#define _LOAD_FAST_6_r23 1046 +#define _LOAD_FAST_7_r01 1047 +#define _LOAD_FAST_7_r12 1048 +#define _LOAD_FAST_7_r23 1049 +#define _LOAD_FAST_AND_CLEAR_r01 1050 +#define _LOAD_FAST_AND_CLEAR_r12 1051 +#define _LOAD_FAST_AND_CLEAR_r23 1052 +#define _LOAD_FAST_BORROW_r01 1053 +#define _LOAD_FAST_BORROW_r12 1054 +#define _LOAD_FAST_BORROW_r23 1055 +#define _LOAD_FAST_BORROW_0_r01 1056 +#define _LOAD_FAST_BORROW_0_r12 1057 +#define _LOAD_FAST_BORROW_0_r23 1058 +#define _LOAD_FAST_BORROW_1_r01 1059 +#define _LOAD_FAST_BORROW_1_r12 1060 +#define _LOAD_FAST_BORROW_1_r23 1061 +#define _LOAD_FAST_BORROW_2_r01 1062 +#define _LOAD_FAST_BORROW_2_r12 1063 +#define _LOAD_FAST_BORROW_2_r23 1064 +#define _LOAD_FAST_BORROW_3_r01 1065 +#define _LOAD_FAST_BORROW_3_r12 1066 +#define _LOAD_FAST_BORROW_3_r23 1067 +#define _LOAD_FAST_BORROW_4_r01 1068 +#define _LOAD_FAST_BORROW_4_r12 1069 +#define _LOAD_FAST_BORROW_4_r23 1070 +#define _LOAD_FAST_BORROW_5_r01 1071 +#define _LOAD_FAST_BORROW_5_r12 1072 +#define _LOAD_FAST_BORROW_5_r23 1073 +#define _LOAD_FAST_BORROW_6_r01 1074 +#define _LOAD_FAST_BORROW_6_r12 1075 +#define _LOAD_FAST_BORROW_6_r23 1076 +#define _LOAD_FAST_BORROW_7_r01 1077 +#define _LOAD_FAST_BORROW_7_r12 1078 +#define _LOAD_FAST_BORROW_7_r23 1079 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1080 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1081 +#define _LOAD_FAST_CHECK_r01 1082 +#define _LOAD_FAST_CHECK_r12 1083 +#define _LOAD_FAST_CHECK_r23 1084 +#define _LOAD_FAST_LOAD_FAST_r02 1085 +#define _LOAD_FAST_LOAD_FAST_r13 1086 +#define _LOAD_FROM_DICT_OR_DEREF_r11 1087 +#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1088 +#define _LOAD_GLOBAL_r00 1089 +#define _LOAD_GLOBAL_BUILTINS_r01 1090 +#define _LOAD_GLOBAL_MODULE_r01 1091 +#define _LOAD_LOCALS_r01 1092 +#define _LOAD_LOCALS_r12 1093 +#define _LOAD_LOCALS_r23 1094 +#define _LOAD_NAME_r01 1095 +#define _LOAD_SMALL_INT_r01 1096 +#define _LOAD_SMALL_INT_r12 1097 +#define _LOAD_SMALL_INT_r23 1098 +#define _LOAD_SMALL_INT_0_r01 1099 +#define _LOAD_SMALL_INT_0_r12 1100 +#define _LOAD_SMALL_INT_0_r23 1101 +#define _LOAD_SMALL_INT_1_r01 1102 +#define _LOAD_SMALL_INT_1_r12 1103 +#define _LOAD_SMALL_INT_1_r23 1104 +#define _LOAD_SMALL_INT_2_r01 1105 +#define _LOAD_SMALL_INT_2_r12 1106 +#define _LOAD_SMALL_INT_2_r23 1107 +#define _LOAD_SMALL_INT_3_r01 1108 +#define _LOAD_SMALL_INT_3_r12 1109 +#define _LOAD_SMALL_INT_3_r23 1110 +#define _LOAD_SPECIAL_r00 1111 +#define _LOAD_SUPER_ATTR_ATTR_r31 1112 +#define _LOAD_SUPER_ATTR_METHOD_r32 1113 +#define _MAKE_CALLARGS_A_TUPLE_r33 1114 +#define _MAKE_CELL_r00 1115 +#define _MAKE_FUNCTION_r11 1116 +#define _MAKE_WARM_r00 1117 +#define _MAKE_WARM_r11 1118 +#define _MAKE_WARM_r22 1119 +#define _MAKE_WARM_r33 1120 +#define _MAP_ADD_r20 1121 +#define _MATCH_CLASS_r31 1122 +#define _MATCH_KEYS_r23 1123 +#define _MATCH_MAPPING_r02 1124 +#define _MATCH_MAPPING_r12 1125 +#define _MATCH_MAPPING_r23 1126 +#define _MATCH_SEQUENCE_r02 1127 +#define _MATCH_SEQUENCE_r12 1128 +#define _MATCH_SEQUENCE_r23 1129 +#define _MAYBE_EXPAND_METHOD_r00 1130 +#define _MAYBE_EXPAND_METHOD_KW_r11 1131 +#define _MONITOR_CALL_r00 1132 +#define _MONITOR_CALL_KW_r11 1133 +#define _MONITOR_JUMP_BACKWARD_r00 1134 +#define _MONITOR_JUMP_BACKWARD_r11 1135 +#define _MONITOR_JUMP_BACKWARD_r22 1136 +#define _MONITOR_JUMP_BACKWARD_r33 1137 +#define _MONITOR_RESUME_r00 1138 +#define _NOP_r00 1139 +#define _NOP_r11 1140 +#define _NOP_r22 1141 +#define _NOP_r33 1142 +#define _POP_CALL_r20 1143 +#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1144 +#define _POP_CALL_ONE_r30 1145 +#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1146 +#define _POP_CALL_TWO_r30 1147 +#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1148 +#define _POP_EXCEPT_r10 1149 +#define _POP_ITER_r20 1150 +#define _POP_JUMP_IF_FALSE_r00 1151 +#define _POP_JUMP_IF_FALSE_r10 1152 +#define _POP_JUMP_IF_FALSE_r21 1153 +#define _POP_JUMP_IF_FALSE_r32 1154 +#define _POP_JUMP_IF_TRUE_r00 1155 +#define _POP_JUMP_IF_TRUE_r10 1156 +#define _POP_JUMP_IF_TRUE_r21 1157 +#define _POP_JUMP_IF_TRUE_r32 1158 +#define _POP_TOP_r10 1159 +#define _POP_TOP_FLOAT_r00 1160 +#define _POP_TOP_FLOAT_r10 1161 +#define _POP_TOP_FLOAT_r21 1162 +#define _POP_TOP_FLOAT_r32 1163 +#define _POP_TOP_INT_r00 1164 +#define _POP_TOP_INT_r10 1165 +#define _POP_TOP_INT_r21 1166 +#define _POP_TOP_INT_r32 1167 +#define _POP_TOP_LOAD_CONST_INLINE_r11 1168 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1169 +#define _POP_TOP_NOP_r00 1170 +#define _POP_TOP_NOP_r10 1171 +#define _POP_TOP_NOP_r21 1172 +#define _POP_TOP_NOP_r32 1173 +#define _POP_TOP_UNICODE_r00 1174 +#define _POP_TOP_UNICODE_r10 1175 +#define _POP_TOP_UNICODE_r21 1176 +#define _POP_TOP_UNICODE_r32 1177 +#define _POP_TWO_r20 1178 +#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1179 +#define _PUSH_EXC_INFO_r02 1180 +#define _PUSH_EXC_INFO_r12 1181 +#define _PUSH_EXC_INFO_r23 1182 +#define _PUSH_FRAME_r10 1183 +#define _PUSH_NULL_r01 1184 +#define _PUSH_NULL_r12 1185 +#define _PUSH_NULL_r23 1186 +#define _PUSH_NULL_CONDITIONAL_r00 1187 +#define _PY_FRAME_GENERAL_r01 1188 +#define _PY_FRAME_KW_r11 1189 +#define _QUICKEN_RESUME_r00 1190 +#define _QUICKEN_RESUME_r11 1191 +#define _QUICKEN_RESUME_r22 1192 +#define _QUICKEN_RESUME_r33 1193 +#define _REPLACE_WITH_TRUE_r11 1194 +#define _RESUME_CHECK_r00 1195 +#define _RESUME_CHECK_r11 1196 +#define _RESUME_CHECK_r22 1197 +#define _RESUME_CHECK_r33 1198 +#define _RETURN_GENERATOR_r01 1199 +#define _RETURN_VALUE_r11 1200 +#define _SAVE_RETURN_OFFSET_r00 1201 +#define _SAVE_RETURN_OFFSET_r11 1202 +#define _SAVE_RETURN_OFFSET_r22 1203 +#define _SAVE_RETURN_OFFSET_r33 1204 +#define _SEND_r22 1205 +#define _SEND_GEN_FRAME_r22 1206 +#define _SETUP_ANNOTATIONS_r00 1207 +#define _SET_ADD_r10 1208 +#define _SET_FUNCTION_ATTRIBUTE_r01 1209 +#define _SET_FUNCTION_ATTRIBUTE_r11 1210 +#define _SET_FUNCTION_ATTRIBUTE_r21 1211 +#define _SET_FUNCTION_ATTRIBUTE_r32 1212 +#define _SET_IP_r00 1213 +#define _SET_IP_r11 1214 +#define _SET_IP_r22 1215 +#define _SET_IP_r33 1216 +#define _SET_UPDATE_r10 1217 +#define _SPILL_OR_RELOAD_r01 1218 +#define _SPILL_OR_RELOAD_r02 1219 +#define _SPILL_OR_RELOAD_r03 1220 +#define _SPILL_OR_RELOAD_r10 1221 +#define _SPILL_OR_RELOAD_r12 1222 +#define _SPILL_OR_RELOAD_r13 1223 +#define _SPILL_OR_RELOAD_r20 1224 +#define _SPILL_OR_RELOAD_r21 1225 +#define _SPILL_OR_RELOAD_r23 1226 +#define _SPILL_OR_RELOAD_r30 1227 +#define _SPILL_OR_RELOAD_r31 1228 +#define _SPILL_OR_RELOAD_r32 1229 +#define _START_EXECUTOR_r00 1230 +#define _STORE_ATTR_r20 1231 +#define _STORE_ATTR_INSTANCE_VALUE_r21 1232 +#define _STORE_ATTR_SLOT_r21 1233 +#define _STORE_ATTR_WITH_HINT_r20 1234 +#define _STORE_DEREF_r10 1235 +#define _STORE_FAST_r10 1236 +#define _STORE_FAST_0_r10 1237 +#define _STORE_FAST_1_r10 1238 +#define _STORE_FAST_2_r10 1239 +#define _STORE_FAST_3_r10 1240 +#define _STORE_FAST_4_r10 1241 +#define _STORE_FAST_5_r10 1242 +#define _STORE_FAST_6_r10 1243 +#define _STORE_FAST_7_r10 1244 +#define _STORE_FAST_LOAD_FAST_r11 1245 +#define _STORE_FAST_STORE_FAST_r20 1246 +#define _STORE_GLOBAL_r10 1247 +#define _STORE_NAME_r10 1248 +#define _STORE_SLICE_r30 1249 +#define _STORE_SUBSCR_r30 1250 +#define _STORE_SUBSCR_DICT_r31 1251 +#define _STORE_SUBSCR_LIST_INT_r32 1252 +#define _SWAP_r11 1253 +#define _SWAP_2_r02 1254 +#define _SWAP_2_r12 1255 +#define _SWAP_2_r22 1256 +#define _SWAP_2_r33 1257 +#define _SWAP_3_r03 1258 +#define _SWAP_3_r13 1259 +#define _SWAP_3_r23 1260 +#define _SWAP_3_r33 1261 +#define _TIER2_RESUME_CHECK_r00 1262 +#define _TIER2_RESUME_CHECK_r11 1263 +#define _TIER2_RESUME_CHECK_r22 1264 +#define _TIER2_RESUME_CHECK_r33 1265 +#define _TO_BOOL_r11 1266 +#define _TO_BOOL_BOOL_r01 1267 +#define _TO_BOOL_BOOL_r11 1268 +#define _TO_BOOL_BOOL_r22 1269 +#define _TO_BOOL_BOOL_r33 1270 +#define _TO_BOOL_INT_r11 1271 +#define _TO_BOOL_LIST_r11 1272 +#define _TO_BOOL_NONE_r01 1273 +#define _TO_BOOL_NONE_r11 1274 +#define _TO_BOOL_NONE_r22 1275 +#define _TO_BOOL_NONE_r33 1276 +#define _TO_BOOL_STR_r11 1277 +#define _TRACE_RECORD_r00 1278 +#define _UNARY_INVERT_r11 1279 +#define _UNARY_NEGATIVE_r11 1280 +#define _UNARY_NOT_r01 1281 +#define _UNARY_NOT_r11 1282 +#define _UNARY_NOT_r22 1283 +#define _UNARY_NOT_r33 1284 +#define _UNPACK_EX_r10 1285 +#define _UNPACK_SEQUENCE_r10 1286 +#define _UNPACK_SEQUENCE_LIST_r10 1287 +#define _UNPACK_SEQUENCE_TUPLE_r10 1288 +#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1289 +#define _WITH_EXCEPT_START_r33 1290 +#define _YIELD_VALUE_r11 1291 +#define MAX_UOP_REGS_ID 1291 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index f600468c321..f0af30df063 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -189,7 +189,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GUARD_TYPE_VERSION] = HAS_EXIT_FLAG, [_GUARD_TYPE_VERSION_AND_LOCK] = HAS_EXIT_FLAG, [_CHECK_MANAGED_OBJECT_HAS_VALUES] = HAS_DEOPT_FLAG, - [_LOAD_ATTR_INSTANCE_VALUE] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_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 | HAS_ESCAPES_FLAG, [_LOAD_ATTR_SLOT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, @@ -1752,11 +1752,11 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { }, }, [_LOAD_ATTR_INSTANCE_VALUE] = { - .best = { 1, 1, 1, 1 }, + .best = { 0, 1, 2, 2 }, .entries = { - { -1, -1, -1 }, - { 1, 1, _LOAD_ATTR_INSTANCE_VALUE_r11 }, - { -1, -1, -1 }, + { 2, 0, _LOAD_ATTR_INSTANCE_VALUE_r02 }, + { 2, 1, _LOAD_ATTR_INSTANCE_VALUE_r12 }, + { 3, 2, _LOAD_ATTR_INSTANCE_VALUE_r23 }, { -1, -1, -1 }, }, }, @@ -3529,7 +3529,9 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_CHECK_MANAGED_OBJECT_HAS_VALUES_r11] = _CHECK_MANAGED_OBJECT_HAS_VALUES, [_CHECK_MANAGED_OBJECT_HAS_VALUES_r22] = _CHECK_MANAGED_OBJECT_HAS_VALUES, [_CHECK_MANAGED_OBJECT_HAS_VALUES_r33] = _CHECK_MANAGED_OBJECT_HAS_VALUES, - [_LOAD_ATTR_INSTANCE_VALUE_r11] = _LOAD_ATTR_INSTANCE_VALUE, + [_LOAD_ATTR_INSTANCE_VALUE_r02] = _LOAD_ATTR_INSTANCE_VALUE, + [_LOAD_ATTR_INSTANCE_VALUE_r12] = _LOAD_ATTR_INSTANCE_VALUE, + [_LOAD_ATTR_INSTANCE_VALUE_r23] = _LOAD_ATTR_INSTANCE_VALUE, [_LOAD_ATTR_MODULE_r11] = _LOAD_ATTR_MODULE, [_LOAD_ATTR_WITH_HINT_r11] = _LOAD_ATTR_WITH_HINT, [_LOAD_ATTR_SLOT_r11] = _LOAD_ATTR_SLOT, @@ -4457,7 +4459,9 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_LOAD_ATTR_CLASS] = "_LOAD_ATTR_CLASS", [_LOAD_ATTR_CLASS_r11] = "_LOAD_ATTR_CLASS_r11", [_LOAD_ATTR_INSTANCE_VALUE] = "_LOAD_ATTR_INSTANCE_VALUE", - [_LOAD_ATTR_INSTANCE_VALUE_r11] = "_LOAD_ATTR_INSTANCE_VALUE_r11", + [_LOAD_ATTR_INSTANCE_VALUE_r02] = "_LOAD_ATTR_INSTANCE_VALUE_r02", + [_LOAD_ATTR_INSTANCE_VALUE_r12] = "_LOAD_ATTR_INSTANCE_VALUE_r12", + [_LOAD_ATTR_INSTANCE_VALUE_r23] = "_LOAD_ATTR_INSTANCE_VALUE_r23", [_LOAD_ATTR_METHOD_LAZY_DICT] = "_LOAD_ATTR_METHOD_LAZY_DICT", [_LOAD_ATTR_METHOD_LAZY_DICT_r02] = "_LOAD_ATTR_METHOD_LAZY_DICT_r02", [_LOAD_ATTR_METHOD_LAZY_DICT_r12] = "_LOAD_ATTR_METHOD_LAZY_DICT_r12", diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 0fa74b9e8fa..3ae87fead94 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2473,6 +2473,25 @@ class TestUopsOptimization(unittest.TestCase): uops = get_opnames(ex) self.assertIn("_POP_TOP_NOP", uops) + def test_load_attr_instance_value(self): + def testfunc(n): + class C(): + pass + c = C() + c.x = n + x = 0 + for _ in range(n): + x = c.x + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_LOAD_ATTR_INSTANCE_VALUE", 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/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-05-52-37.gh-issue-134584.VsfOQR.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-05-52-37.gh-issue-134584.VsfOQR.rst new file mode 100644 index 00000000000..d92f2c166c5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-05-52-37.gh-issue-134584.VsfOQR.rst @@ -0,0 +1 @@ +Eliminate redundant refcounting from ``_LOAD_ATTR_INSTANCE_VALUE``. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 391fa8ea2a9..f126c447492 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2356,7 +2356,7 @@ dummy_func( DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)); } - op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr)) { + op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); @@ -2370,7 +2370,8 @@ dummy_func( attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - PyStackRef_CLOSE(owner); + o = owner; + DEAD(owner); } macro(LOAD_ATTR_INSTANCE_VALUE) = @@ -2378,6 +2379,7 @@ dummy_func( _GUARD_TYPE_VERSION + _CHECK_MANAGED_OBJECT_HAS_VALUES + _LOAD_ATTR_INSTANCE_VALUE + + POP_TOP + unused/5 + _PUSH_NULL_CONDITIONAL; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index b2ce7d0d182..e64934483a2 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -8096,11 +8096,51 @@ break; } - case _LOAD_ATTR_INSTANCE_VALUE_r11: { + case _LOAD_ATTR_INSTANCE_VALUE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + owner = stack_pointer[-1]; + uint16_t offset = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); + 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(value_ptr, attr_o, &attr); + if (!increfed) { + if (true) { + 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_INSTANCE_VALUE_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 offset = (uint16_t)CURRENT_OPERAND0_16(); @@ -8127,18 +8167,54 @@ attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - stack_pointer[0] = attr; - stack_pointer += 1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - 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_INSTANCE_VALUE_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 offset = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); + 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(value_ptr, attr_o, &attr); + if (!increfed) { + if (true) { + 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; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 94846d71830..cd52a20be9f 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -7889,6 +7889,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 @@ -7938,9 +7940,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_CLOSE(owner); + 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 decf98865ae..e9e5bd5db1f 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -570,9 +570,10 @@ dummy_func(void) { assert(oparg >= 2); } - op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr)) { + op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, o)) { attr = sym_new_not_null(ctx); (void)offset; + o = owner; } op(_LOAD_ATTR_MODULE, (dict_version/2, index/1, owner -- attr)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index cdab0fd2ef8..9c61b06d5f7 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1566,11 +1566,19 @@ } case _LOAD_ATTR_INSTANCE_VALUE: { + JitOptRef owner; JitOptRef attr; + JitOptRef o; + owner = stack_pointer[-1]; uint16_t offset = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); (void)offset; + o = owner; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; + stack_pointer[0] = o; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } From 8c87bcd7f2fd7ea5f731b5fd64ab261814ec892f Mon Sep 17 00:00:00 2001 From: ivonastojanovic <80911834+ivonastojanovic@users.noreply.github.com> Date: Tue, 16 Dec 2025 20:58:56 +0000 Subject: [PATCH 06/80] gh-138122: Update Tachyon dark theme colors (#142841) --- .../sampling/_heatmap_assets/heatmap.css | 12 ++++++++++++ Lib/profiling/sampling/_shared_assets/base.css | 18 +++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap.css b/Lib/profiling/sampling/_heatmap_assets/heatmap.css index 4fba9d866ac..65a13d7245d 100644 --- a/Lib/profiling/sampling/_heatmap_assets/heatmap.css +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap.css @@ -5,6 +5,18 @@ This file extends the shared foundation with heatmap-specific styles. ========================================================================== */ +/* Heatmap heat colors - using base.css colors with 60% opacity */ +[data-theme="dark"] { + --heat-1: rgba(90, 123, 167, 0.60); + --heat-2: rgba(106, 148, 168, 0.60); + --heat-3: rgba(122, 172, 172, 0.60); + --heat-4: rgba(142, 196, 152, 0.60); + --heat-5: rgba(168, 216, 136, 0.60); + --heat-6: rgba(200, 222, 122, 0.60); + --heat-7: rgba(244, 212, 93, 0.60); + --heat-8: rgba(255, 122, 69, 0.60); +} + /* -------------------------------------------------------------------------- Layout Overrides (Heatmap-specific) -------------------------------------------------------------------------- */ diff --git a/Lib/profiling/sampling/_shared_assets/base.css b/Lib/profiling/sampling/_shared_assets/base.css index 39bdd52e943..cb59a0f77c5 100644 --- a/Lib/profiling/sampling/_shared_assets/base.css +++ b/Lib/profiling/sampling/_shared_assets/base.css @@ -124,15 +124,15 @@ --header-gradient: linear-gradient(135deg, #21262d 0%, #30363d 100%); - /* Dark mode heat palette - muted colors that provide sufficient contrast with light text */ - --heat-1: rgba(74, 123, 167, 0.35); - --heat-2: rgba(90, 159, 168, 0.38); - --heat-3: rgba(106, 181, 181, 0.40); - --heat-4: rgba(126, 196, 136, 0.42); - --heat-5: rgba(160, 216, 120, 0.45); - --heat-6: rgba(196, 222, 106, 0.48); - --heat-7: rgba(244, 212, 77, 0.50); - --heat-8: rgba(255, 107, 53, 0.55); + /* Dark mode heat palette - cool to warm gradient for visualization */ + --heat-1: rgba(90, 123, 167, 1); + --heat-2: rgba(106, 148, 168, 1); + --heat-3: rgba(122, 172, 172, 1); + --heat-4: rgba(142, 196, 152, 1); + --heat-5: rgba(168, 216, 136, 1); + --heat-6: rgba(200, 222, 122, 1); + --heat-7: rgba(244, 212, 93, 1); + --heat-8: rgba(255, 122, 69, 1); /* Code view specific - dark mode */ --code-bg: #0d1117; From 434525398196db692196661d15e678b3d514aa54 Mon Sep 17 00:00:00 2001 From: Nadeshiko Manju Date: Wed, 17 Dec 2025 07:21:04 +0900 Subject: [PATCH 07/80] gh-134584: Eliminate redundant refcounting from `_STORE_ATTR_WITH_HINT` (GH-142767) Signed-off-by: Manjusaka --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_ids.h | 2 +- Include/internal/pycore_uop_metadata.h | 6 +++--- Lib/test/test_capi/test_opt.py | 20 +++++++++++++++++++ ...-12-16-05-24-24.gh-issue-134584.tJ1usH.rst | 1 + Python/bytecodes.c | 8 +++++--- Python/executor_cases.c.h | 14 ++++++++----- Python/generated_cases.c.h | 15 ++++++++++++-- Python/optimizer_bytecodes.c | 5 +++++ Python/optimizer_cases.c.h | 13 ++++++++++-- 10 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-05-24-24.gh-issue-134584.tJ1usH.rst diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index c0e3725db1d..a4f1f50e96d 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1484,7 +1484,7 @@ _PyOpcode_macro_expansion[256] = { [STORE_ATTR] = { .nuops = 1, .uops = { { _STORE_ATTR, OPARG_SIMPLE, 3 } } }, [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION_AND_LOCK, 2, 1 }, { _GUARD_DORV_NO_DICT, OPARG_SIMPLE, 3 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 } } }, [STORE_ATTR_SLOT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 } } }, - [STORE_ATTR_WITH_HINT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_WITH_HINT, 1, 3 } } }, + [STORE_ATTR_WITH_HINT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_WITH_HINT, 1, 3 }, { _POP_TOP, OPARG_SIMPLE, 4 } } }, [STORE_DEREF] = { .nuops = 1, .uops = { { _STORE_DEREF, OPARG_SIMPLE, 0 } } }, [STORE_FAST] = { .nuops = 1, .uops = { { _STORE_FAST, OPARG_SIMPLE, 0 } } }, [STORE_FAST_LOAD_FAST] = { .nuops = 2, .uops = { { _STORE_FAST, OPARG_TOP, 0 }, { _LOAD_FAST, OPARG_BOTTOM, 0 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 5519e14e902..8678ccf5e1b 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -1038,7 +1038,7 @@ extern "C" { #define _STORE_ATTR_r20 1231 #define _STORE_ATTR_INSTANCE_VALUE_r21 1232 #define _STORE_ATTR_SLOT_r21 1233 -#define _STORE_ATTR_WITH_HINT_r20 1234 +#define _STORE_ATTR_WITH_HINT_r21 1234 #define _STORE_DEREF_r10 1235 #define _STORE_FAST_r10 1236 #define _STORE_FAST_0_r10 1237 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index f0af30df063..c6d3670b193 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -1837,7 +1837,7 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { .entries = { { -1, -1, -1 }, { -1, -1, -1 }, - { 0, 2, _STORE_ATTR_WITH_HINT_r20 }, + { 1, 2, _STORE_ATTR_WITH_HINT_r21 }, { -1, -1, -1 }, }, }, @@ -3546,7 +3546,7 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_GUARD_DORV_NO_DICT_r22] = _GUARD_DORV_NO_DICT, [_GUARD_DORV_NO_DICT_r33] = _GUARD_DORV_NO_DICT, [_STORE_ATTR_INSTANCE_VALUE_r21] = _STORE_ATTR_INSTANCE_VALUE, - [_STORE_ATTR_WITH_HINT_r20] = _STORE_ATTR_WITH_HINT, + [_STORE_ATTR_WITH_HINT_r21] = _STORE_ATTR_WITH_HINT, [_STORE_ATTR_SLOT_r21] = _STORE_ATTR_SLOT, [_COMPARE_OP_r21] = _COMPARE_OP, [_COMPARE_OP_FLOAT_r01] = _COMPARE_OP_FLOAT, @@ -4786,7 +4786,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_STORE_ATTR_SLOT] = "_STORE_ATTR_SLOT", [_STORE_ATTR_SLOT_r21] = "_STORE_ATTR_SLOT_r21", [_STORE_ATTR_WITH_HINT] = "_STORE_ATTR_WITH_HINT", - [_STORE_ATTR_WITH_HINT_r20] = "_STORE_ATTR_WITH_HINT_r20", + [_STORE_ATTR_WITH_HINT_r21] = "_STORE_ATTR_WITH_HINT_r21", [_STORE_DEREF] = "_STORE_DEREF", [_STORE_DEREF_r10] = "_STORE_DEREF_r10", [_STORE_FAST] = "_STORE_FAST", diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 3ae87fead94..bed8d1d52e9 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2612,6 +2612,26 @@ class TestUopsOptimization(unittest.TestCase): self.assertNotIn("_POP_TOP", uops) self.assertIn("_POP_TOP_NOP", uops) + def test_store_attr_with_hint(self): + def testfunc(n): + class C: + pass + c = C() + for i in range(_testinternalcapi.SHARED_KEYS_MAX_SIZE - 1): + setattr(c, f"_{i}", None) + + for i in range(n): + c.x = i + return c.x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD - 1) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_STORE_ATTR_WITH_HINT", uops) + self.assertNotIn("_POP_TOP", uops) + self.assertIn("_POP_TOP_NOP", uops) + def test_store_subscr_int(self): def testfunc(n): l = [0, 0, 0, 0] diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-05-24-24.gh-issue-134584.tJ1usH.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-05-24-24.gh-issue-134584.tJ1usH.rst new file mode 100644 index 00000000000..aa096fc827d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-05-24-24.gh-issue-134584.tJ1usH.rst @@ -0,0 +1 @@ +Eliminate redundant refcounting from ``_STORE_ATTR_WITH_HINT``. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index f126c447492..25e730f7e6e 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2603,7 +2603,7 @@ dummy_func( _STORE_ATTR_INSTANCE_VALUE + POP_TOP; - op(_STORE_ATTR_WITH_HINT, (hint/1, value, owner --)) { + op(_STORE_ATTR_WITH_HINT, (hint/1, value, owner -- o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner_o); @@ -2633,14 +2633,16 @@ dummy_func( // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. STAT_INC(STORE_ATTR, hit); - PyStackRef_CLOSE(owner); + o = owner; + DEAD(owner); Py_XDECREF(old_value); } macro(STORE_ATTR_WITH_HINT) = unused/1 + _GUARD_TYPE_VERSION + - _STORE_ATTR_WITH_HINT; + _STORE_ATTR_WITH_HINT + + POP_TOP; op(_STORE_ATTR_SLOT, (index/1, value, owner -- o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index e64934483a2..55c4495f2dc 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -8775,11 +8775,12 @@ break; } - case _STORE_ATTR_WITH_HINT_r20: { + case _STORE_ATTR_WITH_HINT_r21: { CHECK_CURRENT_CACHED_VALUES(2); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef owner; _PyStackRef value; + _PyStackRef o; _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; oparg = CURRENT_OPARG(); @@ -8848,16 +8849,19 @@ FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(dict); STAT_INC(STORE_ATTR, hit); - stack_pointer += -2; + o = owner; + stack_pointer[-2] = o; + stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); - _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache0 = o; _tos_cache1 = PyStackRef_ZERO_BITS; _tos_cache2 = PyStackRef_ZERO_BITS; - SET_CURRENT_CACHED_VALUES(0); + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + 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 cd52a20be9f..3c18960a39d 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -10747,6 +10747,7 @@ static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); _PyStackRef owner; _PyStackRef value; + _PyStackRef o; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION { @@ -10812,13 +10813,23 @@ FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(dict); STAT_INC(STORE_ATTR, hit); - stack_pointer += -2; + o = owner; + stack_pointer[-2] = o; + stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); } + // _POP_TOP + { + value = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } DISPATCH(); } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index e9e5bd5db1f..31ec88e1c84 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -104,6 +104,11 @@ dummy_func(void) { o = owner; } + op(_STORE_ATTR_WITH_HINT, (hint/1, value, owner -- o)) { + (void)value; + o = owner; + } + op(_STORE_FAST, (value --)) { GETLOCAL(oparg) = value; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 9c61b06d5f7..6fde87a17fb 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1699,8 +1699,17 @@ } case _STORE_ATTR_WITH_HINT: { - CHECK_STACK_BOUNDS(-2); - stack_pointer += -2; + JitOptRef owner; + JitOptRef value; + JitOptRef o; + owner = stack_pointer[-1]; + value = stack_pointer[-2]; + uint16_t hint = (uint16_t)this_instr->operand0; + (void)value; + o = owner; + CHECK_STACK_BOUNDS(-1); + stack_pointer[-2] = o; + stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } From 89729f2ef7f9473d9e4b898b2ed49a065a67f450 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 08:12:32 +0800 Subject: [PATCH 08/80] gh-142543: Mark tracer functions as Py_NO_INLINE (GH-142846) --- .../2025-12-16-23-26-41.gh-issue-142543.wJKjBs.rst | 1 + Python/optimizer.c | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-23-26-41.gh-issue-142543.wJKjBs.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-23-26-41.gh-issue-142543.wJKjBs.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-23-26-41.gh-issue-142543.wJKjBs.rst new file mode 100644 index 00000000000..0897127fec7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-23-26-41.gh-issue-142543.wJKjBs.rst @@ -0,0 +1 @@ +Fix a stack overflow on Clang JIT build configurations with full LTO. diff --git a/Python/optimizer.c b/Python/optimizer.c index b215e43128f..18f54f9bc23 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -582,7 +582,8 @@ is_terminator(const _PyUOpInstruction *uop) /* Returns 1 on success (added to trace), 0 on trace end. */ -int +// gh-142543: inlining this function causes stack overflows +Py_NO_INLINE int _PyJit_translate_single_bytecode_to_trace( PyThreadState *tstate, _PyInterpreterFrame *frame, @@ -994,7 +995,8 @@ full: } // Returns 0 for do not enter tracing, 1 on enter tracing. -int +// gh-142543: inlining this function causes stack overflows +Py_NO_INLINE int _PyJit_TryInitializeTracing( PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *curr_instr, _Py_CODEUNIT *start_instr, _Py_CODEUNIT *close_loop_instr, int curr_stackdepth, int chain_depth, @@ -1066,7 +1068,7 @@ _PyJit_TryInitializeTracing( return 1; } -void +Py_NO_INLINE void _PyJit_FinalizeTracing(PyThreadState *tstate) { _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; From c35b812e773493f88631cd1e8be65d3a7c3b47ae Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 17 Dec 2025 02:38:00 -0500 Subject: [PATCH 09/80] gh-142836: Avoid /proc fd pipes on Solaris (#142853) --- Lib/test/test_pdb.py | 13 ++++++++++++- .../2025-12-17-02-02-57.gh-issue-142836.mR-fvK.rst | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tests/2025-12-17-02-02-57.gh-issue-142836.mR-fvK.rst diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index f4c870036a7..5cba34ff8ba 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3563,10 +3563,22 @@ class PdbTestCase(unittest.TestCase): def _fd_dir_for_pipe_targets(self): """Return a directory exposing live file descriptors, if any.""" + return self._proc_fd_dir() or self._dev_fd_dir() + + def _proc_fd_dir(self): + """Return /proc-backed fd dir when it can be used for pipes.""" + # GH-142836: Opening /proc/self/fd entries for pipes raises EACCES on + # Solaris, so prefer other mechanisms there. + if sys.platform.startswith("sunos"): + return None + proc_fd = "/proc/self/fd" if os.path.isdir(proc_fd) and os.path.exists(os.path.join(proc_fd, '0')): return proc_fd + return None + def _dev_fd_dir(self): + """Return /dev-backed fd dir when usable.""" dev_fd = "/dev/fd" if os.path.isdir(dev_fd) and os.path.exists(os.path.join(dev_fd, '0')): if sys.platform.startswith("freebsd"): @@ -3576,7 +3588,6 @@ class PdbTestCase(unittest.TestCase): except FileNotFoundError: return None return dev_fd - return None def test_find_function_empty_file(self): diff --git a/Misc/NEWS.d/next/Tests/2025-12-17-02-02-57.gh-issue-142836.mR-fvK.rst b/Misc/NEWS.d/next/Tests/2025-12-17-02-02-57.gh-issue-142836.mR-fvK.rst new file mode 100644 index 00000000000..dd84ce9839f --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2025-12-17-02-02-57.gh-issue-142836.mR-fvK.rst @@ -0,0 +1 @@ +Accommodated Solaris in ``test_pdb.test_script_target_anonymous_pipe``. From 4fd006e71247601e9a84e0fa04f96bed5129f09e Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 17 Dec 2025 13:17:12 +0530 Subject: [PATCH 10/80] gh-142752: add more thread safety tests for mock (#142791) --- .../testmock/testthreadingmock.py | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/Lib/test/test_unittest/testmock/testthreadingmock.py b/Lib/test/test_unittest/testmock/testthreadingmock.py index 3603995b090..dda4916434e 100644 --- a/Lib/test/test_unittest/testmock/testthreadingmock.py +++ b/Lib/test/test_unittest/testmock/testthreadingmock.py @@ -219,5 +219,81 @@ class TestThreadingMock(unittest.TestCase): self.assertEqual(m.call_count, LOOPS * THREADS) + def test_call_args_thread_safe(self): + m = ThreadingMock() + LOOPS = 100 + THREADS = 10 + def test_function(thread_id): + for i in range(LOOPS): + m(thread_id, i) + + oldswitchinterval = sys.getswitchinterval() + setswitchinterval(1e-6) + try: + threads = [ + threading.Thread(target=test_function, args=(thread_id,)) + for thread_id in range(THREADS) + ] + with threading_helper.start_threads(threads): + pass + finally: + sys.setswitchinterval(oldswitchinterval) + expected_calls = { + (thread_id, i) + for thread_id in range(THREADS) + for i in range(LOOPS) + } + self.assertSetEqual({call.args for call in m.call_args_list}, expected_calls) + + def test_method_calls_thread_safe(self): + m = ThreadingMock() + LOOPS = 100 + THREADS = 10 + def test_function(thread_id): + for i in range(LOOPS): + getattr(m, f"method_{thread_id}")(i) + + oldswitchinterval = sys.getswitchinterval() + setswitchinterval(1e-6) + try: + threads = [ + threading.Thread(target=test_function, args=(thread_id,)) + for thread_id in range(THREADS) + ] + with threading_helper.start_threads(threads): + pass + finally: + sys.setswitchinterval(oldswitchinterval) + for thread_id in range(THREADS): + self.assertEqual(getattr(m, f"method_{thread_id}").call_count, LOOPS) + self.assertEqual({call.args for call in getattr(m, f"method_{thread_id}").call_args_list}, + {(i,) for i in range(LOOPS)}) + + def test_mock_calls_thread_safe(self): + m = ThreadingMock() + LOOPS = 100 + THREADS = 10 + def test_function(thread_id): + for i in range(LOOPS): + m(thread_id, i) + + oldswitchinterval = sys.getswitchinterval() + setswitchinterval(1e-6) + try: + threads = [ + threading.Thread(target=test_function, args=(thread_id,)) + for thread_id in range(THREADS) + ] + with threading_helper.start_threads(threads): + pass + finally: + sys.setswitchinterval(oldswitchinterval) + expected_calls = { + (thread_id, i) + for thread_id in range(THREADS) + for i in range(LOOPS) + } + self.assertSetEqual({call.args for call in m.mock_calls}, expected_calls) + if __name__ == "__main__": unittest.main() From 8307a14d0edc14ae2f775995720e4b6d6dc865b3 Mon Sep 17 00:00:00 2001 From: wangxiaolei Date: Wed, 17 Dec 2025 16:35:08 +0800 Subject: [PATCH 11/80] gh-142783: Fix possible use after free in zoneinfo module (GH-142790) --- Lib/test/test_zoneinfo/test_zoneinfo.py | 20 +++++++++++++++++++ ...-12-16-14-49-19.gh-issue-142783.VPV1ig.rst | 1 + Modules/_zoneinfo.c | 16 ++++++++------- 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-16-14-49-19.gh-issue-142783.VPV1ig.rst diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index fad741e477b..8f3ca59c9ef 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -1551,6 +1551,26 @@ class ZoneInfoCacheTest(TzPathUserMixin, ZoneInfoTestBase): except CustomError: pass + def test_weak_cache_descriptor_use_after_free(self): + class BombDescriptor: + def __get__(self, obj, owner): + return {} + + class EvilZoneInfo(self.klass): + pass + + # Must be set after the class creation. + EvilZoneInfo._weak_cache = BombDescriptor() + + key = "America/Los_Angeles" + zone1 = EvilZoneInfo(key) + self.assertEqual(str(zone1), key) + + EvilZoneInfo.clear_cache() + zone2 = EvilZoneInfo(key) + self.assertEqual(str(zone2), key) + self.assertIsNot(zone2, zone1) + class CZoneInfoCacheTest(ZoneInfoCacheTest): module = c_zoneinfo diff --git a/Misc/NEWS.d/next/Library/2025-12-16-14-49-19.gh-issue-142783.VPV1ig.rst b/Misc/NEWS.d/next/Library/2025-12-16-14-49-19.gh-issue-142783.VPV1ig.rst new file mode 100644 index 00000000000..f014771ae9a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-16-14-49-19.gh-issue-142783.VPV1ig.rst @@ -0,0 +1 @@ +Fix zoneinfo use-after-free with descriptor _weak_cache. a descriptor as _weak_cache could cause crashes during object creation. The fix ensures proper reference counting for descriptor-provided objects. diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index b99be073db5..e07dfd19efa 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -292,16 +292,11 @@ static PyObject * get_weak_cache(zoneinfo_state *state, PyTypeObject *type) { if (type == state->ZoneInfoType) { + Py_INCREF(state->ZONEINFO_WEAK_CACHE); return state->ZONEINFO_WEAK_CACHE; } else { - PyObject *cache = - PyObject_GetAttrString((PyObject *)type, "_weak_cache"); - // We are assuming that the type lives at least as long as the function - // that calls get_weak_cache, and that it holds a reference to the - // cache, so we'll return a "borrowed reference". - Py_XDECREF(cache); - return cache; + return PyObject_GetAttrString((PyObject *)type, "_weak_cache"); } } @@ -328,6 +323,7 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key) PyObject *weak_cache = get_weak_cache(state, type); instance = PyObject_CallMethod(weak_cache, "get", "O", key, Py_None); if (instance == NULL) { + Py_DECREF(weak_cache); return NULL; } @@ -335,6 +331,7 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key) Py_DECREF(instance); PyObject *tmp = zoneinfo_new_instance(state, type, key); if (tmp == NULL) { + Py_DECREF(weak_cache); return NULL; } @@ -342,12 +339,14 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key) PyObject_CallMethod(weak_cache, "setdefault", "OO", key, tmp); Py_DECREF(tmp); if (instance == NULL) { + Py_DECREF(weak_cache); return NULL; } ((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE; } update_strong_cache(state, type, key, instance); + Py_DECREF(weak_cache); return instance; } @@ -510,12 +509,14 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, PyObject *item = NULL; PyObject *pop = PyUnicode_FromString("pop"); if (pop == NULL) { + Py_DECREF(weak_cache); return NULL; } PyObject *iter = PyObject_GetIter(only_keys); if (iter == NULL) { Py_DECREF(pop); + Py_DECREF(weak_cache); return NULL; } @@ -540,6 +541,7 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, Py_DECREF(pop); } + Py_DECREF(weak_cache); if (PyErr_Occurred()) { return NULL; } From 454485e564f637eefd980568c090808a681a91d0 Mon Sep 17 00:00:00 2001 From: yihong Date: Wed, 17 Dec 2025 17:44:47 +0800 Subject: [PATCH 12/80] gh-139743: Avoid print twice verbose version for sqlite tests (GH-142850) Signed-off-by: yihong0618 --- Lib/test/test_sqlite3/__init__.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_sqlite3/__init__.py b/Lib/test/test_sqlite3/__init__.py index 78a1e2078a5..145f3b80024 100644 --- a/Lib/test/test_sqlite3/__init__.py +++ b/Lib/test/test_sqlite3/__init__.py @@ -6,9 +6,14 @@ import_helper.import_module('_sqlite3') import os import sqlite3 +# make sure only print once +_printed_version = False + # Implement the unittest "load tests" protocol. -def load_tests(*args): - if verbose: +def load_tests(loader, tests, pattern): + global _printed_version + if verbose and not _printed_version: print(f"test_sqlite3: testing with SQLite version {sqlite3.sqlite_version}") + _printed_version = True pkg_dir = os.path.dirname(__file__) - return load_package_tests(pkg_dir, *args) + return load_package_tests(pkg_dir, loader, tests, pattern) From 1e9a0ee6823023a9bdaa60e6fcc71327c604a0fb Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Wed, 17 Dec 2025 12:09:51 +0100 Subject: [PATCH 13/80] gh-140374: Add glossary entries related to multithreading (#140375) --------- Co-authored-by: Daniele Parmeggiani <8658291+dpdani@users.noreply.github.com> --- Doc/glossary.rst | 172 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 164 insertions(+), 8 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 3a01df99c38..68035c2dfb5 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -134,6 +134,14 @@ Glossary iterator's :meth:`~object.__anext__` method until it raises a :exc:`StopAsyncIteration` exception. Introduced by :pep:`492`. + atomic operation + An operation that appears to execute as a single, indivisible step: no + other thread can observe it half-done, and its effects become visible all + at once. Python does not guarantee that high-level statements are atomic + (for example, ``x += 1`` performs multiple bytecode operations and is not + atomic). Atomicity is only guaranteed where explicitly documented. See + also :term:`race condition` and :term:`data race`. + attached thread state A :term:`thread state` that is active for the current OS thread. @@ -289,6 +297,22 @@ Glossary advanced mathematical feature. If you're not aware of a need for them, it's almost certain you can safely ignore them. + concurrency + The ability of a computer program to perform multiple tasks at the same + time. Python provides libraries for writing programs that make use of + different forms of concurrency. :mod:`asyncio` is a library for dealing + with asynchronous tasks and coroutines. :mod:`threading` provides + access to operating system threads and :mod:`multiprocessing` to + operating system processes. Multi-core processors can execute threads and + processes on different CPU cores at the same time (see + :term:`parallelism`). + + concurrent modification + When multiple threads modify shared data at the same time. Concurrent + modification without proper synchronization can cause + :term:`race conditions `, and might also trigger a + :term:`data race `, data corruption, or both. + context This term has different meanings depending on where and how it is used. Some common meanings: @@ -363,6 +387,28 @@ Glossary the :term:`cyclic garbage collector ` is to identify these groups and break the reference cycles so that the memory can be reclaimed. + data race + A situation where multiple threads access the same memory location + concurrently, at least one of the accesses is a write, and the threads + do not use any synchronization to control their access. Data races + lead to :term:`non-deterministic` behavior and can cause data corruption. + Proper use of :term:`locks ` and other :term:`synchronization primitives + ` prevents data races. Note that data races + can only happen in native code, but that :term:`native code` might be + exposed in a Python API. See also :term:`race condition` and + :term:`thread-safe`. + + deadlock + A situation in which two or more tasks (threads, processes, or coroutines) + wait indefinitely for each other to release resources or complete actions, + preventing any from making progress. For example, if thread A holds lock + 1 and waits for lock 2, while thread B holds lock 2 and waits for lock 1, + both threads will wait indefinitely. In Python this often arises from + acquiring multiple locks in conflicting orders or from circular + join/await dependencies. Deadlocks can be avoided by always acquiring + multiple :term:`locks ` in a consistent order. See also + :term:`lock` and :term:`reentrant`. + decorator A function returning another function, usually applied as a function transformation using the ``@wrapper`` syntax. Common examples for @@ -662,6 +708,14 @@ Glossary requires the GIL to be held in order to use it. This refers to having an :term:`attached thread state`. + global state + Data that is accessible throughout a program, such as module-level + variables, class variables, or C static variables in :term:`extension modules + `. In multi-threaded programs, global state shared + between threads typically requires synchronization to avoid + :term:`race conditions ` and + :term:`data races `. + hash-based pyc A bytecode cache file that uses the hash rather than the last-modified time of the corresponding source file to determine its validity. See @@ -706,7 +760,9 @@ Glossary tuples. Such an object cannot be altered. A new object has to be created if a different value has to be stored. They play an important role in places where a constant hash value is needed, for example as a key - in a dictionary. + in a dictionary. Immutable objects are inherently :term:`thread-safe` + because their state cannot be modified after creation, eliminating concerns + about improperly synchronized :term:`concurrent modification`. import path A list of locations (or :term:`path entries `) that are @@ -796,8 +852,9 @@ Glossary CPython does not consistently apply the requirement that an iterator define :meth:`~iterator.__iter__`. - And also please note that the free-threading CPython does not guarantee - the thread-safety of iterator operations. + And also please note that :term:`free-threaded ` + CPython does not guarantee :term:`thread-safe` behavior of iterator + operations. key function @@ -835,10 +892,11 @@ Glossary :keyword:`if` statements. In a multi-threaded environment, the LBYL approach can risk introducing a - race condition between "the looking" and "the leaping". For example, the - code, ``if key in mapping: return mapping[key]`` can fail if another + :term:`race condition` between "the looking" and "the leaping". For example, + the code, ``if key in mapping: return mapping[key]`` can fail if another thread removes *key* from *mapping* after the test, but before the lookup. - This issue can be solved with locks or by using the EAFP approach. + This issue can be solved with :term:`locks ` or by using the + :term:`EAFP` approach. See also :term:`thread-safe`. lexical analyzer @@ -857,6 +915,19 @@ Glossary clause is optional. If omitted, all elements in ``range(256)`` are processed. + lock + A :term:`synchronization primitive` that allows only one thread at a + time to access a shared resource. A thread must acquire a lock before + accessing the protected resource and release it afterward. If a thread + attempts to acquire a lock that is already held by another thread, it + will block until the lock becomes available. Python's :mod:`threading` + module provides :class:`~threading.Lock` (a basic lock) and + :class:`~threading.RLock` (a :term:`reentrant` lock). Locks are used + to prevent :term:`race conditions ` and ensure + :term:`thread-safe` access to shared data. Alternative design patterns + to locks exist such as queues, producer/consumer patterns, and + thread-local state. See also :term:`deadlock`, and :term:`reentrant`. + loader An object that loads a module. It must define the :meth:`!exec_module` and :meth:`!create_module` methods @@ -942,8 +1013,11 @@ Glossary See :term:`method resolution order`. mutable - Mutable objects can change their value but keep their :func:`id`. See - also :term:`immutable`. + An :term:`object` with state that is allowed to change during the course + of the program. In multi-threaded programs, mutable objects that are + shared between threads require careful synchronization to avoid + :term:`race conditions `. See also :term:`immutable`, + :term:`thread-safe`, and :term:`concurrent modification`. named tuple The term "named tuple" applies to any type or class that inherits from @@ -995,6 +1069,13 @@ Glossary See also :term:`module`. + native code + Code that is compiled to machine instructions and runs directly on the + processor, as opposed to code that is interpreted or runs in a virtual + machine. In the context of Python, native code typically refers to + C, C++, Rust or Fortran code in :term:`extension modules ` + that can be called from Python. See also :term:`extension module`. + nested scope The ability to refer to a variable in an enclosing definition. For instance, a function defined inside another function can refer to @@ -1011,6 +1092,15 @@ Glossary properties, :meth:`~object.__getattribute__`, class methods, and static methods. + non-deterministic + Behavior where the outcome of a program can vary between executions with + the same inputs. In multi-threaded programs, non-deterministic behavior + often results from :term:`race conditions ` where the + relative timing or interleaving of threads affects the result. + Proper synchronization using :term:`locks ` and other + :term:`synchronization primitives ` helps + ensure deterministic behavior. + object Any data with state (attributes or value) and defined behavior (methods). Also the ultimate base class of any :term:`new-style @@ -1041,6 +1131,16 @@ Glossary See also :term:`regular package` and :term:`namespace package`. + parallelism + Executing multiple operations at the same time (e.g. on multiple CPU + cores). In Python builds with the + :term:`global interpreter lock (GIL) `, only one + thread runs Python bytecode at a time, so taking advantage of multiple + CPU cores typically involves multiple processes + (e.g. :mod:`multiprocessing`) or native extensions that release the GIL. + In :term:`free-threaded ` Python, multiple Python threads + can run Python code simultaneously on different cores. + parameter A named entity in a :term:`function` (or method) definition that specifies an :term:`argument` (or in some cases, arguments) that the @@ -1215,6 +1315,18 @@ Glossary >>> email.mime.text.__name__ 'email.mime.text' + race condition + A condition of a program where the its behavior + depends on the relative timing or ordering of events, particularly in + multi-threaded programs. Race conditions can lead to + :term:`non-deterministic` behavior and bugs that are difficult to + reproduce. A :term:`data race` is a specific type of race condition + involving unsynchronized access to shared memory. The :term:`LBYL` + coding style is particularly susceptible to race conditions in + multi-threaded code. Using :term:`locks ` and other + :term:`synchronization primitives ` + helps prevent race conditions. + reference count The number of references to an object. When the reference count of an object drops to zero, it is deallocated. Some objects are @@ -1236,6 +1348,25 @@ Glossary See also :term:`namespace package`. + reentrant + A property of a function or :term:`lock` that allows it to be called or + acquired multiple times by the same thread without causing errors or a + :term:`deadlock`. + + For functions, reentrancy means the function can be safely called again + before a previous invocation has completed, which is important when + functions may be called recursively or from signal handlers. Thread-unsafe + functions may be :term:`non-deterministic` if they're called reentrantly in a + multithreaded program. + + For locks, Python's :class:`threading.RLock` (reentrant lock) is + reentrant, meaning a thread that already holds the lock can acquire it + again without blocking. In contrast, :class:`threading.Lock` is not + reentrant - attempting to acquire it twice from the same thread will cause + a deadlock. + + See also :term:`lock` and :term:`deadlock`. + REPL An acronym for the "read–eval–print loop", another name for the :term:`interactive` interpreter shell. @@ -1340,6 +1471,18 @@ Glossary See also :term:`borrowed reference`. + synchronization primitive + A basic building block for coordinating (synchronizing) the execution of + multiple threads to ensure :term:`thread-safe` access to shared resources. + Python's :mod:`threading` module provides several synchronization primitives + including :class:`~threading.Lock`, :class:`~threading.RLock`, + :class:`~threading.Semaphore`, :class:`~threading.Condition`, + :class:`~threading.Event`, and :class:`~threading.Barrier`. Additionally, + the :mod:`queue` module provides multi-producer, multi-consumer queues + that are especially useful in multithreaded programs. These + primitives help prevent :term:`race conditions ` and + coordinate thread execution. See also :term:`lock`. + t-string t-strings String literals prefixed with ``t`` or ``T`` are commonly called @@ -1392,6 +1535,19 @@ Glossary See :ref:`Thread State and the Global Interpreter Lock ` for more information. + thread-safe + A module, function, or class that behaves correctly when used by multiple + threads concurrently. Thread-safe code uses appropriate + :term:`synchronization primitives ` like + :term:`locks ` to protect shared mutable state, or is designed + to avoid shared mutable state entirely. In the + :term:`free-threaded ` build, built-in types like + :class:`dict`, :class:`list`, and :class:`set` use internal locking + to make many operations thread-safe, although thread safety is not + necessarily guaranteed. Code that is not thread-safe may experience + :term:`race conditions ` and :term:`data races ` + when used in multi-threaded programs. + token A small unit of source code, generated by the From 248eb3efb3cad7799ef9b4a2dd77a66d1ae65c11 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 17 Dec 2025 14:23:30 +0300 Subject: [PATCH 14/80] gh-142859: Add `Tools/check-c-api-docs` to mypy check (#142860) --- .github/workflows/mypy.yml | 2 ++ Tools/check-c-api-docs/mypy.ini | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 Tools/check-c-api-docs/mypy.ini diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index fac0fa8aba3..8810730e193 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -26,6 +26,7 @@ on: - "Tools/build/update_file.py" - "Tools/build/verify_ensurepip_wheels.py" - "Tools/cases_generator/**" + - "Tools/check-c-api-docs/**" - "Tools/clinic/**" - "Tools/jit/**" - "Tools/peg_generator/**" @@ -58,6 +59,7 @@ jobs: "Lib/tomllib", "Tools/build", "Tools/cases_generator", + "Tools/check-c-api-docs", "Tools/clinic", "Tools/jit", "Tools/peg_generator", diff --git a/Tools/check-c-api-docs/mypy.ini b/Tools/check-c-api-docs/mypy.ini new file mode 100644 index 00000000000..f42eb2836e2 --- /dev/null +++ b/Tools/check-c-api-docs/mypy.ini @@ -0,0 +1,19 @@ +[mypy] +files = Tools/check-c-api-docs/ +pretty = True + +# We need `_colorize` import: +mypy_path = $MYPY_CONFIG_FILE_DIR/../../Misc/mypy + +# Make sure Python can still be built +# using Python 3.13 for `PYTHON_FOR_REGEN`... +python_version = 3.13 + +# ...And be strict: +strict = True +extra_checks = True +enable_error_code = + ignore-without-code, + redundant-expr, + truthy-bool, + possibly-undefined, From c7dcb2652083a702a023881bc328ecaa5f1f2ac5 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 17 Dec 2025 12:07:07 +0000 Subject: [PATCH 15/80] GH-142621: JIT: Avoid memory load for symbols within 4GB on AArch64 (GH-142820) --- Python/jit.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Python/jit.c b/Python/jit.c index 602d7a519bd..ccafe0ce497 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -432,6 +432,15 @@ patch_aarch64_33rx(unsigned char *location, uint64_t value) loc32[1] = 0xF2A00000 | (get_bits(relaxed, 16, 16) << 5) | reg; return; } + int64_t page_delta = (relaxed >> 12) - ((uintptr_t)location >> 12); + if (page_delta >= -(1L << 20) && + page_delta < (1L << 20)) + { + // adrp reg, AAA; ldr reg, [reg + BBB] -> adrp reg, AAA; add reg, reg, BBB + patch_aarch64_21rx(location, relaxed); + loc32[1] = 0x91000000 | get_bits(relaxed, 0, 12) << 10 | reg << 5 | reg; + return; + } relaxed = value - (uintptr_t)location; if ((relaxed & 0x3) == 0 && (int64_t)relaxed >= -(1L << 19) && From 1fc3039d7172095d90df489c7a5e555f7128ee90 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 17 Dec 2025 04:14:44 -0800 Subject: [PATCH 16/80] gh-139038: Add JIT What's New for 3.15 (#142845) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Ken Jin --- Doc/whatsnew/3.15.rst | 86 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 24a51f87c0f..53d613ffb3a 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -73,6 +73,7 @@ Summary -- Release highlights ` * :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object ` +* :ref:`The JIT compiler has been significantly upgraded ` * :ref:`Improved error messages ` @@ -850,6 +851,91 @@ csv (Contributed by Maurycy Pawłowski-Wieroński in :gh:`137628`.) +.. _whatsnew315-jit: + +Upgraded JIT compiler +===================== + +Results from the `pyperformance `__ +benchmark suite report +`3-4% `__ +geometric mean performance improvement for the JIT over the standard CPython +interpreter built with all optimizations enabled. The speedups for JIT +builds versus no JIT builds range from roughly 20% slowdown to over +100% speedup (ignoring the ``unpack_sequence`` microbenchmark) on +x86-64 Linux and AArch64 macOS systems. + +.. attention:: + These results are not yet final. + +The major upgrades to the JIT are: + +* LLVM 21 build-time dependency +* New tracing frontend +* Basic register allocation in the JIT +* More JIT optimizations +* Better machine code generation + +.. rubric:: LLVM 21 build-time dependency + +The JIT compiler now uses LLVM 21 for build-time stencil generation. As +always, LLVM is only needed when building CPython with the JIT enabled; +end users running Python do not need LLVM installed. Instructions for +installing LLVM can be found in the `JIT compiler documentation +`__ +for all supported platforms. + +(Contributed by Savannah Ostrowski in :gh:`140973`.) + +.. rubric:: A new tracing frontend + +The JIT compiler now supports significantly more bytecode operations and +control flow than in Python 3.14, enabling speedups on a wider variety of +code. For example, simple Python object creation is now understood by the +3.15 JIT compiler. Overloaded operations and generators are also partially +supported. This was made possible by an overhauled JIT tracing frontend +that records actual execution paths through code, rather than estimating +them as the previous implementation did. + +(Contributed by Ken Jin in :gh:`139109`. Support for Windows added by +Mark Shannon in :gh:`141703`.) + +.. rubric:: Basic register allocation in the JIT + +A basic form of register allocation has been added to the JIT compiler's +optimizer. This allows the JIT compiler to avoid certain stack operations +altogether and instead operate on registers. This allows the JIT to produce +more efficient traces by avoiding reads and writes to memory. + +(Contributed by Mark Shannon in :gh:`135379`.) + +.. rubric:: More JIT optimizations + +More `constant-propagation `__ +is now performed. This means when the JIT compiler detects that certain user +code results in constants, the code can be simplified by the JIT. + +(Contributed by Ken Jin and Savannah Ostrowski in :gh:`132732`.) + +The JIT avoids :term:`reference count`\ s where possible. This generally +reduces the cost of most operations in Python. + +(Contributed by Ken Jin, Donghee Na, Nadeshiko Manju, Savannah Ostrowski, +Noam Cohen, Tomas Roun, PuQing in :gh:`134584`.) + +.. rubric:: Better machine code generation + +The JIT compiler's machine code generator now produces better machine code +for x86-64 and AArch64 macOS and Linux targets. In general, users should +experience lower memory usage for generated machine code and more efficient +machine code versus the old JIT. + +(Contributed by Brandt Bucher in :gh:`136528` and :gh:`136528`. +Implementation for AArch64 contributed by Mark Shannon in :gh:`139855`. +Additional optimizations for AArch64 contributed by Mark Shannon and +Diego Russo in :gh:`140683` and :gh:`142305`.) + + Removed ======= From d4095f25e8573efb63196ae96a2f7ba8b5f06dce Mon Sep 17 00:00:00 2001 From: Keming Date: Wed, 17 Dec 2025 22:15:22 +0800 Subject: [PATCH 17/80] gh-142654: show the clear error message when sampling on an unknown PID in tachyon (#142655) Co-authored-by: Pablo Galindo Salgado --- Lib/profiling/sampling/__main__.py | 7 ++ Lib/profiling/sampling/cli.py | 9 ++- Lib/profiling/sampling/errors.py | 19 +++++ Lib/profiling/sampling/sample.py | 72 +++++++++++-------- .../test_sampling_profiler/test_cli.py | 26 +++++-- .../test_integration.py | 8 +-- .../test_sampling_profiler/test_modes.py | 4 ++ ...-12-13-10-34-59.gh-issue-142654.fmm974.rst | 2 + 8 files changed, 104 insertions(+), 43 deletions(-) create mode 100644 Lib/profiling/sampling/errors.py create mode 100644 Misc/NEWS.d/next/Library/2025-12-13-10-34-59.gh-issue-142654.fmm974.rst diff --git a/Lib/profiling/sampling/__main__.py b/Lib/profiling/sampling/__main__.py index 47bd3a0113e..a45b645eae0 100644 --- a/Lib/profiling/sampling/__main__.py +++ b/Lib/profiling/sampling/__main__.py @@ -46,6 +46,7 @@ system restrictions or missing privileges. """ from .cli import main +from .errors import SamplingUnknownProcessError, SamplingModuleNotFoundError, SamplingScriptNotFoundError def handle_permission_error(): """Handle PermissionError by displaying appropriate error message.""" @@ -64,3 +65,9 @@ if __name__ == '__main__': main() except PermissionError: handle_permission_error() + except SamplingUnknownProcessError as err: + print(f"Tachyon cannot find the process: {err}", file=sys.stderr) + sys.exit(1) + except (SamplingModuleNotFoundError, SamplingScriptNotFoundError) as err: + print(f"Tachyon cannot find the target: {err}", file=sys.stderr) + sys.exit(1) diff --git a/Lib/profiling/sampling/cli.py b/Lib/profiling/sampling/cli.py index e1ff3758c0d..554167e43f5 100644 --- a/Lib/profiling/sampling/cli.py +++ b/Lib/profiling/sampling/cli.py @@ -10,7 +10,8 @@ import sys import time from contextlib import nullcontext -from .sample import sample, sample_live +from .errors import SamplingUnknownProcessError, SamplingModuleNotFoundError, SamplingScriptNotFoundError +from .sample import sample, sample_live, _is_process_running from .pstats_collector import PstatsCollector from .stack_collector import CollapsedStackCollector, FlamegraphCollector from .heatmap_collector import HeatmapCollector @@ -743,6 +744,8 @@ Examples: def _handle_attach(args): """Handle the 'attach' command.""" + if not _is_process_running(args.pid): + raise SamplingUnknownProcessError(args.pid) # Check if live mode is requested if args.live: _handle_live_attach(args, args.pid) @@ -792,13 +795,13 @@ def _handle_run(args): added_cwd = True try: if importlib.util.find_spec(args.target) is None: - sys.exit(f"Error: Module not found: {args.target}") + raise SamplingModuleNotFoundError(args.target) finally: if added_cwd: sys.path.remove(cwd) else: if not os.path.exists(args.target): - sys.exit(f"Error: Script not found: {args.target}") + raise SamplingScriptNotFoundError(args.target) # Check if live mode is requested if args.live: diff --git a/Lib/profiling/sampling/errors.py b/Lib/profiling/sampling/errors.py new file mode 100644 index 00000000000..0832ad2d438 --- /dev/null +++ b/Lib/profiling/sampling/errors.py @@ -0,0 +1,19 @@ +"""Custom exceptions for the sampling profiler.""" + +class SamplingProfilerError(Exception): + """Base exception for sampling profiler errors.""" + +class SamplingUnknownProcessError(SamplingProfilerError): + def __init__(self, pid): + self.pid = pid + super().__init__(f"Process with PID '{pid}' does not exist.") + +class SamplingScriptNotFoundError(SamplingProfilerError): + def __init__(self, script_path): + self.script_path = script_path + super().__init__(f"Script '{script_path}' not found.") + +class SamplingModuleNotFoundError(SamplingProfilerError): + def __init__(self, module_name): + self.module_name = module_name + super().__init__(f"Module '{module_name}' not found.") diff --git a/Lib/profiling/sampling/sample.py b/Lib/profiling/sampling/sample.py index 294ec3003fc..d4c3b577a17 100644 --- a/Lib/profiling/sampling/sample.py +++ b/Lib/profiling/sampling/sample.py @@ -34,24 +34,30 @@ class SampleProfiler: self.all_threads = all_threads self.mode = mode # Store mode for later use self.collect_stats = collect_stats - if _FREE_THREADED_BUILD: - self.unwinder = _remote_debugging.RemoteUnwinder( - self.pid, all_threads=self.all_threads, mode=mode, native=native, gc=gc, - opcodes=opcodes, skip_non_matching_threads=skip_non_matching_threads, - cache_frames=True, stats=collect_stats - ) - else: - only_active_threads = bool(self.all_threads) - self.unwinder = _remote_debugging.RemoteUnwinder( - self.pid, only_active_thread=only_active_threads, mode=mode, native=native, gc=gc, - opcodes=opcodes, skip_non_matching_threads=skip_non_matching_threads, - cache_frames=True, stats=collect_stats - ) + try: + self.unwinder = self._new_unwinder(native, gc, opcodes, skip_non_matching_threads) + except RuntimeError as err: + raise SystemExit(err) from err # Track sample intervals and total sample count self.sample_intervals = deque(maxlen=100) self.total_samples = 0 self.realtime_stats = False + def _new_unwinder(self, native, gc, opcodes, skip_non_matching_threads): + if _FREE_THREADED_BUILD: + unwinder = _remote_debugging.RemoteUnwinder( + self.pid, all_threads=self.all_threads, mode=self.mode, native=native, gc=gc, + opcodes=opcodes, skip_non_matching_threads=skip_non_matching_threads, + cache_frames=True, stats=self.collect_stats + ) + else: + unwinder = _remote_debugging.RemoteUnwinder( + self.pid, only_active_thread=bool(self.all_threads), mode=self.mode, native=native, gc=gc, + opcodes=opcodes, skip_non_matching_threads=skip_non_matching_threads, + cache_frames=True, stats=self.collect_stats + ) + return unwinder + def sample(self, collector, duration_sec=10, *, async_aware=False): sample_interval_sec = self.sample_interval_usec / 1_000_000 running_time = 0 @@ -86,7 +92,7 @@ class SampleProfiler: collector.collect_failed_sample() errors += 1 except Exception as e: - if not self._is_process_running(): + if not _is_process_running(self.pid): break raise e from None @@ -148,22 +154,6 @@ class SampleProfiler: f"({(expected_samples - num_samples) / expected_samples * 100:.2f}%)" ) - def _is_process_running(self): - if sys.platform == "linux" or sys.platform == "darwin": - try: - os.kill(self.pid, 0) - return True - except ProcessLookupError: - return False - elif sys.platform == "win32": - try: - _remote_debugging.RemoteUnwinder(self.pid) - except Exception: - return False - return True - else: - raise ValueError(f"Unsupported platform: {sys.platform}") - def _print_realtime_stats(self): """Print real-time sampling statistics.""" if len(self.sample_intervals) < 2: @@ -279,6 +269,28 @@ class SampleProfiler: print(f" {ANSIColors.YELLOW}Stale cache invalidations: {stale_invalidations}{ANSIColors.RESET}") +def _is_process_running(pid): + if pid <= 0: + return False + if os.name == "posix": + try: + os.kill(pid, 0) + return True + except ProcessLookupError: + return False + except PermissionError: + # EPERM means process exists but we can't signal it + return True + elif sys.platform == "win32": + try: + _remote_debugging.RemoteUnwinder(pid) + except Exception: + return False + return True + else: + raise ValueError(f"Unsupported platform: {sys.platform}") + + def sample( pid, collector, diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_cli.py b/Lib/test/test_profiling/test_sampling_profiler/test_cli.py index 4434335130c..9b2b16d6e19 100644 --- a/Lib/test/test_profiling/test_sampling_profiler/test_cli.py +++ b/Lib/test/test_profiling/test_sampling_profiler/test_cli.py @@ -16,6 +16,7 @@ except ImportError: from test.support import is_emscripten, requires_remote_subprocess_debugging from profiling.sampling.cli import main +from profiling.sampling.errors import SamplingScriptNotFoundError, SamplingModuleNotFoundError, SamplingUnknownProcessError class TestSampleProfilerCLI(unittest.TestCase): @@ -203,12 +204,12 @@ class TestSampleProfilerCLI(unittest.TestCase): with ( mock.patch("sys.argv", test_args), mock.patch("sys.stderr", io.StringIO()) as mock_stderr, - self.assertRaises(SystemExit) as cm, + self.assertRaises(SamplingScriptNotFoundError) as cm, ): main() # Verify the error is about the non-existent script - self.assertIn("12345", str(cm.exception.code)) + self.assertIn("12345", str(cm.exception)) def test_cli_no_target_specified(self): # In new CLI, must specify a subcommand @@ -436,6 +437,7 @@ class TestSampleProfilerCLI(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): main() @@ -475,6 +477,7 @@ class TestSampleProfilerCLI(unittest.TestCase): for test_args, expected_filename, expected_format in test_cases: with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): main() @@ -513,6 +516,7 @@ class TestSampleProfilerCLI(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): main() @@ -534,6 +538,7 @@ class TestSampleProfilerCLI(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): main() @@ -547,6 +552,7 @@ class TestSampleProfilerCLI(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): main() @@ -562,6 +568,7 @@ class TestSampleProfilerCLI(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): main() @@ -576,6 +583,7 @@ class TestSampleProfilerCLI(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): main() @@ -697,14 +705,20 @@ class TestSampleProfilerCLI(unittest.TestCase): def test_run_nonexistent_script_exits_cleanly(self): """Test that running a non-existent script exits with a clean error.""" with mock.patch("sys.argv", ["profiling.sampling.cli", "run", "/nonexistent/script.py"]): - with self.assertRaises(SystemExit) as cm: + with self.assertRaisesRegex(SamplingScriptNotFoundError, "Script '[\\w/.]+' not found."): main() - self.assertIn("Script not found", str(cm.exception.code)) @unittest.skipIf(is_emscripten, "subprocess not available") def test_run_nonexistent_module_exits_cleanly(self): """Test that running a non-existent module exits with a clean error.""" with mock.patch("sys.argv", ["profiling.sampling.cli", "run", "-m", "nonexistent_module_xyz"]): - with self.assertRaises(SystemExit) as cm: + with self.assertRaisesRegex(SamplingModuleNotFoundError, "Module '[\\w/.]+' not found."): main() - self.assertIn("Module not found", str(cm.exception.code)) + + def test_cli_attach_nonexistent_pid(self): + fake_pid = "99999" + with mock.patch("sys.argv", ["profiling.sampling.cli", "attach", fake_pid]): + with self.assertRaises(SamplingUnknownProcessError) as cm: + main() + + self.assertIn(fake_pid, str(cm.exception)) diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_integration.py b/Lib/test/test_profiling/test_sampling_profiler/test_integration.py index 08a96d7eb45..e7f6c59d5cb 100644 --- a/Lib/test/test_profiling/test_sampling_profiler/test_integration.py +++ b/Lib/test/test_profiling/test_sampling_profiler/test_integration.py @@ -17,7 +17,7 @@ try: import profiling.sampling.sample from profiling.sampling.pstats_collector import PstatsCollector from profiling.sampling.stack_collector import CollapsedStackCollector - from profiling.sampling.sample import SampleProfiler + from profiling.sampling.sample import SampleProfiler, _is_process_running except ImportError: raise unittest.SkipTest( "Test only runs when _remote_debugging is available" @@ -602,7 +602,7 @@ do_work() @requires_remote_subprocess_debugging() class TestSampleProfilerErrorHandling(unittest.TestCase): def test_invalid_pid(self): - with self.assertRaises((OSError, RuntimeError)): + with self.assertRaises((SystemExit, PermissionError)): collector = PstatsCollector(sample_interval_usec=100, skip_idle=False) profiling.sampling.sample.sample(-1, collector, duration_sec=1) @@ -638,7 +638,7 @@ class TestSampleProfilerErrorHandling(unittest.TestCase): sample_interval_usec=1000, all_threads=False, ) - self.assertTrue(profiler._is_process_running()) + self.assertTrue(_is_process_running(profiler.pid)) self.assertIsNotNone(profiler.unwinder.get_stack_trace()) subproc.process.kill() subproc.process.wait() @@ -647,7 +647,7 @@ class TestSampleProfilerErrorHandling(unittest.TestCase): ) # Exit the context manager to ensure the process is terminated - self.assertFalse(profiler._is_process_running()) + self.assertFalse(_is_process_running(profiler.pid)) self.assertRaises( ProcessLookupError, profiler.unwinder.get_stack_trace ) diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_modes.py b/Lib/test/test_profiling/test_sampling_profiler/test_modes.py index f1293544776..247416389da 100644 --- a/Lib/test/test_profiling/test_sampling_profiler/test_modes.py +++ b/Lib/test/test_profiling/test_sampling_profiler/test_modes.py @@ -252,6 +252,7 @@ class TestGilModeFiltering(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): try: @@ -313,6 +314,7 @@ class TestGilModeFiltering(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): try: @@ -432,6 +434,7 @@ class TestExceptionModeFiltering(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): try: @@ -493,6 +496,7 @@ class TestExceptionModeFiltering(unittest.TestCase): with ( mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), mock.patch("profiling.sampling.cli.sample") as mock_sample, ): try: diff --git a/Misc/NEWS.d/next/Library/2025-12-13-10-34-59.gh-issue-142654.fmm974.rst b/Misc/NEWS.d/next/Library/2025-12-13-10-34-59.gh-issue-142654.fmm974.rst new file mode 100644 index 00000000000..7bb14cb499d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-13-10-34-59.gh-issue-142654.fmm974.rst @@ -0,0 +1,2 @@ +Show the clearer error message when using ``profiling.sampling`` on an +unknown PID. From 49627dc991aae534c09fe55cf64e3fc6cef71e56 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 22:21:02 +0800 Subject: [PATCH 18/80] Use other name for JIT contributor (#142877) --- Doc/whatsnew/3.15.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 53d613ffb3a..7e032fe5df2 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -920,7 +920,7 @@ code results in constants, the code can be simplified by the JIT. The JIT avoids :term:`reference count`\ s where possible. This generally reduces the cost of most operations in Python. -(Contributed by Ken Jin, Donghee Na, Nadeshiko Manju, Savannah Ostrowski, +(Contributed by Ken Jin, Donghee Na, Zheao Li, Savannah Ostrowski, Noam Cohen, Tomas Roun, PuQing in :gh:`134584`.) .. rubric:: Better machine code generation From 2b466c47c333106dc9522ab77898e6972e25a2c6 Mon Sep 17 00:00:00 2001 From: Benjamin Johnson Date: Wed, 17 Dec 2025 07:09:57 -0800 Subject: [PATCH 19/80] gh-112127: Fix possible use-after-free in atexit.unregister() (GH-114092) Co-authored-by: Serhiy Storchaka --- Lib/test/_test_atexit.py | 13 +++++++++++++ Misc/ACKS | 1 + .../2025-12-17-14-41-09.gh-issue-112127.13OHQk.rst | 2 ++ Modules/atexitmodule.c | 3 ++- 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-17-14-41-09.gh-issue-112127.13OHQk.rst diff --git a/Lib/test/_test_atexit.py b/Lib/test/_test_atexit.py index f618c1fcbca..490b0686a0c 100644 --- a/Lib/test/_test_atexit.py +++ b/Lib/test/_test_atexit.py @@ -135,6 +135,19 @@ class GeneralTest(unittest.TestCase): finally: atexit.unregister(func) + def test_eq_unregister_clear(self): + # Issue #112127: callback's __eq__ may call unregister or _clear + class Evil: + def __eq__(self, other): + action(other) + return NotImplemented + + for action in atexit.unregister, lambda o: atexit._clear(): + with self.subTest(action=action): + atexit.register(lambda: None) + atexit.unregister(Evil()) + atexit._clear() + if __name__ == "__main__": unittest.main() diff --git a/Misc/ACKS b/Misc/ACKS index e3927ff0b33..a14089a39cc 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -908,6 +908,7 @@ Jim Jewett Pedro Diaz Jimenez Orjan Johansen Fredrik Johansson +Benjamin Johnson Benjamin K. Johnson Gregory K. Johnson Kent Johnson diff --git a/Misc/NEWS.d/next/Library/2025-12-17-14-41-09.gh-issue-112127.13OHQk.rst b/Misc/NEWS.d/next/Library/2025-12-17-14-41-09.gh-issue-112127.13OHQk.rst new file mode 100644 index 00000000000..c983683ebd5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-17-14-41-09.gh-issue-112127.13OHQk.rst @@ -0,0 +1,2 @@ +Fix possible use-after-free in :func:`atexit.unregister` when the callback +is unregistered during comparison. diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c index 4536b03fbc4..f81f0b57247 100644 --- a/Modules/atexitmodule.c +++ b/Modules/atexitmodule.c @@ -257,10 +257,11 @@ static int atexit_unregister_locked(PyObject *callbacks, PyObject *func) { for (Py_ssize_t i = 0; i < PyList_GET_SIZE(callbacks); ++i) { - PyObject *tuple = PyList_GET_ITEM(callbacks, i); + PyObject *tuple = Py_NewRef(PyList_GET_ITEM(callbacks, i)); assert(PyTuple_CheckExact(tuple)); PyObject *to_compare = PyTuple_GET_ITEM(tuple, 0); int cmp = PyObject_RichCompareBool(func, to_compare, Py_EQ); + Py_DECREF(tuple); if (cmp < 0) { return -1; From 568a819f677238095441d17b9d8ac6d3ea4e3314 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Wed, 17 Dec 2025 15:12:28 +0000 Subject: [PATCH 20/80] gh-138122: Validate base frame before caching in remote debugging frame cache (#142852) --- .../test_integration.py | 95 +++++++++++++++++++ ...-12-17-03-03-12.gh-issue-138122.m3EF9E.rst | 4 + Modules/_remote_debugging/_remote_debugging.h | 9 +- Modules/_remote_debugging/frame_cache.c | 16 +++- Modules/_remote_debugging/frames.c | 41 +++++--- Modules/_remote_debugging/threads.c | 4 +- 6 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-17-03-03-12.gh-issue-138122.m3EF9E.rst diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_integration.py b/Lib/test/test_profiling/test_sampling_profiler/test_integration.py index e7f6c59d5cb..b82474858dd 100644 --- a/Lib/test/test_profiling/test_sampling_profiler/test_integration.py +++ b/Lib/test/test_profiling/test_sampling_profiler/test_integration.py @@ -863,3 +863,98 @@ asyncio.run(supervisor()) self.assertGreater(cpu_percentage, 90.0, f"cpu_leaf should dominate samples in 'running' mode, " f"got {cpu_percentage:.1f}% ({cpu_leaf_samples}/{total})") + + +def _generate_deep_generators_script(chain_depth=20, recurse_depth=150): + """Generate a script with deep nested generators for stress testing.""" + lines = [ + 'import sys', + 'sys.setrecursionlimit(5000)', + '', + ] + # Generate chain of yield-from functions + for i in range(chain_depth - 1): + lines.extend([ + f'def deep_yield_chain_{i}(n):', + f' yield ("L{i}", n)', + f' yield from deep_yield_chain_{i + 1}(n)', + '', + ]) + # Last chain function calls recursive_diver + lines.extend([ + f'def deep_yield_chain_{chain_depth - 1}(n):', + f' yield ("L{chain_depth - 1}", n)', + f' yield from recursive_diver(n, {chain_depth})', + '', + 'def recursive_diver(n, depth):', + ' yield (f"DIVE_{depth}", n)', + f' if depth < {recurse_depth}:', + ' yield from recursive_diver(n, depth + 1)', + ' else:', + ' for i in range(5):', + ' yield (f"BOTTOM_{depth}", i)', + '', + 'def oscillating_generator(iterations=1000):', + ' for i in range(iterations):', + ' yield ("OSCILLATE", i)', + ' yield from deep_yield_chain_0(i)', + '', + 'def run_forever():', + ' while True:', + ' for _ in oscillating_generator(10):', + ' pass', + '', + '_test_sock.sendall(b"working")', + 'run_forever()', + ]) + return '\n'.join(lines) + + +@requires_remote_subprocess_debugging() +class TestDeepGeneratorFrameCache(unittest.TestCase): + """Test frame cache consistency with deep oscillating generator stacks.""" + + def test_all_stacks_share_same_base_frame(self): + """Verify all sampled stacks reach the entry point function. + + When profiling deep generators that oscillate up and down the call + stack, every sample should include the entry point function + (run_forever) in its call chain. If the frame cache stores + incomplete stacks, some samples will be missing this base function, + causing broken flamegraphs. + """ + script = _generate_deep_generators_script() + with test_subprocess(script, wait_for_working=True) as subproc: + collector = CollapsedStackCollector(sample_interval_usec=1, skip_idle=False) + + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=2, + ) + + samples_with_entry_point = 0 + samples_without_entry_point = 0 + total_samples = 0 + + for (call_tree, _thread_id), count in collector.stack_counter.items(): + total_samples += count + if call_tree: + has_entry_point = call_tree and call_tree[0][2] == "" + if has_entry_point: + samples_with_entry_point += count + else: + samples_without_entry_point += count + + self.assertGreater(total_samples, 100, + f"Expected at least 100 samples, got {total_samples}") + + self.assertEqual(samples_without_entry_point, 0, + f"Found {samples_without_entry_point}/{total_samples} samples " + f"missing the entry point function 'run_forever'. This indicates " + f"incomplete stacks are being returned, likely due to frame cache " + f"storing partial stack traces.") diff --git a/Misc/NEWS.d/next/Library/2025-12-17-03-03-12.gh-issue-138122.m3EF9E.rst b/Misc/NEWS.d/next/Library/2025-12-17-03-03-12.gh-issue-138122.m3EF9E.rst new file mode 100644 index 00000000000..e33a761aa61 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-17-03-03-12.gh-issue-138122.m3EF9E.rst @@ -0,0 +1,4 @@ +Fix incomplete stack traces in the Tachyon profiler's frame cache when +profiling code with deeply nested generators. The frame cache now validates +that stack traces reach the base frame before caching, preventing broken +flamegraphs. Patch by Pablo Galindo. diff --git a/Modules/_remote_debugging/_remote_debugging.h b/Modules/_remote_debugging/_remote_debugging.h index 2f3efedd1e0..663479f235a 100644 --- a/Modules/_remote_debugging/_remote_debugging.h +++ b/Modules/_remote_debugging/_remote_debugging.h @@ -429,7 +429,8 @@ extern int process_frame_chain( int *stopped_at_cached_frame, uintptr_t *frame_addrs, Py_ssize_t *num_addrs, - Py_ssize_t max_addrs + Py_ssize_t max_addrs, + uintptr_t *out_last_frame_addr ); /* Frame cache functions */ @@ -447,18 +448,22 @@ extern int frame_cache_lookup_and_extend( Py_ssize_t *num_addrs, Py_ssize_t max_addrs); // Returns: 1 = stored, 0 = not stored (graceful), -1 = error +// Only stores complete stacks that reach base_frame_addr extern int frame_cache_store( RemoteUnwinderObject *unwinder, uint64_t thread_id, PyObject *frame_list, const uintptr_t *addrs, - Py_ssize_t num_addrs); + Py_ssize_t num_addrs, + uintptr_t base_frame_addr, + uintptr_t last_frame_visited); extern int collect_frames_with_cache( RemoteUnwinderObject *unwinder, uintptr_t frame_addr, StackChunkList *chunks, PyObject *frame_info, + uintptr_t base_frame_addr, uintptr_t gc_frame, uintptr_t last_profiled_frame, uint64_t thread_id); diff --git a/Modules/_remote_debugging/frame_cache.c b/Modules/_remote_debugging/frame_cache.c index 4598b9dc353..ab7891445e0 100644 --- a/Modules/_remote_debugging/frame_cache.c +++ b/Modules/_remote_debugging/frame_cache.c @@ -194,6 +194,7 @@ frame_cache_lookup_and_extend( } // Store frame list with addresses in cache +// Only stores complete stacks that reach base_frame_addr (validation done internally) // Returns: 1 = stored successfully, 0 = not stored (graceful degradation), -1 = error int frame_cache_store( @@ -201,12 +202,25 @@ frame_cache_store( uint64_t thread_id, PyObject *frame_list, const uintptr_t *addrs, - Py_ssize_t num_addrs) + Py_ssize_t num_addrs, + uintptr_t base_frame_addr, + uintptr_t last_frame_visited) { if (!unwinder->frame_cache || thread_id == 0) { return 0; } + // Validate we have a complete stack before caching. + // Only cache if last_frame_visited matches base_frame_addr (the sentinel + // at the bottom of the stack). Note: we use last_frame_visited rather than + // addrs[num_addrs-1] because the base frame is visited but not added to the + // addrs array (it returns frame==NULL from is_frame_valid due to + // owner==FRAME_OWNED_BY_INTERPRETER). + if (base_frame_addr != 0 && last_frame_visited != base_frame_addr) { + // Incomplete stack - don't cache (graceful degradation) + return 0; + } + // Clamp to max frames if (num_addrs > FRAME_CACHE_MAX_FRAMES) { num_addrs = FRAME_CACHE_MAX_FRAMES; diff --git a/Modules/_remote_debugging/frames.c b/Modules/_remote_debugging/frames.c index abde60c4576..47e34e9f945 100644 --- a/Modules/_remote_debugging/frames.c +++ b/Modules/_remote_debugging/frames.c @@ -265,7 +265,8 @@ process_frame_chain( int *stopped_at_cached_frame, uintptr_t *frame_addrs, // optional: C array to receive frame addresses Py_ssize_t *num_addrs, // in/out: current count / updated count - Py_ssize_t max_addrs) // max capacity of frame_addrs array + Py_ssize_t max_addrs, // max capacity of frame_addrs array + uintptr_t *out_last_frame_addr) // optional: receives last frame address visited { uintptr_t frame_addr = initial_frame_addr; uintptr_t prev_frame_addr = 0; @@ -273,10 +274,13 @@ process_frame_chain( const size_t MAX_FRAMES = 1024 + 512; size_t frame_count = 0; - // Initialize output flag + // Initialize output parameters if (stopped_at_cached_frame) { *stopped_at_cached_frame = 0; } + if (out_last_frame_addr) { + *out_last_frame_addr = 0; + } // Quick check: if current_frame == last_profiled_frame, entire stack is unchanged if (last_profiled_frame != 0 && initial_frame_addr == last_profiled_frame) { @@ -390,6 +394,11 @@ process_frame_chain( return -1; } + // Set output parameter for caller (needed for cache validation) + if (out_last_frame_addr) { + *out_last_frame_addr = last_frame_addr; + } + return 0; } @@ -537,6 +546,7 @@ collect_frames_with_cache( uintptr_t frame_addr, StackChunkList *chunks, PyObject *frame_info, + uintptr_t base_frame_addr, uintptr_t gc_frame, uintptr_t last_profiled_frame, uint64_t thread_id) @@ -551,11 +561,13 @@ collect_frames_with_cache( uintptr_t addrs[FRAME_CACHE_MAX_FRAMES]; Py_ssize_t num_addrs = 0; Py_ssize_t frames_before = PyList_GET_SIZE(frame_info); + uintptr_t last_frame_visited = 0; int stopped_at_cached = 0; - if (process_frame_chain(unwinder, frame_addr, chunks, frame_info, 0, gc_frame, + if (process_frame_chain(unwinder, frame_addr, chunks, frame_info, base_frame_addr, gc_frame, last_profiled_frame, &stopped_at_cached, - addrs, &num_addrs, FRAME_CACHE_MAX_FRAMES) < 0) { + addrs, &num_addrs, FRAME_CACHE_MAX_FRAMES, + &last_frame_visited) < 0) { return -1; } @@ -575,23 +587,28 @@ collect_frames_with_cache( // Cache miss - continue walking from last_profiled_frame to get the rest STATS_INC(unwinder, frame_cache_misses); Py_ssize_t frames_before_walk = PyList_GET_SIZE(frame_info); - if (process_frame_chain(unwinder, last_profiled_frame, chunks, frame_info, 0, gc_frame, - 0, NULL, addrs, &num_addrs, FRAME_CACHE_MAX_FRAMES) < 0) { + if (process_frame_chain(unwinder, last_profiled_frame, chunks, frame_info, base_frame_addr, gc_frame, + 0, NULL, addrs, &num_addrs, FRAME_CACHE_MAX_FRAMES, + &last_frame_visited) < 0) { return -1; } STATS_ADD(unwinder, frames_read_from_memory, PyList_GET_SIZE(frame_info) - frames_before_walk); } else { - // Partial cache hit + // Partial cache hit - cache was validated when stored, so we trust it STATS_INC(unwinder, frame_cache_partial_hits); STATS_ADD(unwinder, frames_read_from_cache, PyList_GET_SIZE(frame_info) - frames_before_cache); } - } else if (last_profiled_frame == 0) { - // No cache involvement (no last_profiled_frame or cache disabled) - STATS_INC(unwinder, frame_cache_misses); + } else { + if (last_profiled_frame == 0) { + // No cache involvement (no last_profiled_frame or cache disabled) + STATS_INC(unwinder, frame_cache_misses); + } } - // Store in cache (frame_cache_store handles truncation if num_addrs > FRAME_CACHE_MAX_FRAMES) - if (frame_cache_store(unwinder, thread_id, frame_info, addrs, num_addrs) < 0) { + // Store in cache - frame_cache_store validates internally that we have a + // complete stack (reached base_frame_addr) before actually storing + if (frame_cache_store(unwinder, thread_id, frame_info, addrs, num_addrs, + base_frame_addr, last_frame_visited) < 0) { return -1; } diff --git a/Modules/_remote_debugging/threads.c b/Modules/_remote_debugging/threads.c index 81c13ea48e3..6db774ecfc2 100644 --- a/Modules/_remote_debugging/threads.c +++ b/Modules/_remote_debugging/threads.c @@ -430,7 +430,7 @@ unwind_stack_for_thread( uintptr_t last_profiled_frame = GET_MEMBER(uintptr_t, ts, unwinder->debug_offsets.thread_state.last_profiled_frame); if (collect_frames_with_cache(unwinder, frame_addr, &chunks, frame_info, - gc_frame, last_profiled_frame, tid) < 0) { + base_frame_addr, gc_frame, last_profiled_frame, tid) < 0) { set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to collect frames"); goto error; } @@ -444,7 +444,7 @@ unwind_stack_for_thread( } else { // No caching - process entire frame chain with base_frame validation if (process_frame_chain(unwinder, frame_addr, &chunks, frame_info, - base_frame_addr, gc_frame, 0, NULL, NULL, NULL, 0) < 0) { + base_frame_addr, gc_frame, 0, NULL, NULL, NULL, 0, NULL) < 0) { set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process frame chain"); goto error; } From 7d81eab923f71f6b7e80f130bc3ad5ca613b88fd Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 17 Dec 2025 16:33:09 +0100 Subject: [PATCH 21/80] gh-142225: Add PyABIInfo_VAR to to _testcapimodule & _testinternalcapi (GH-142833) --- Modules/_testcapimodule.c | 3 +++ Modules/_testinternalcapi.c | 3 +++ Tools/c-analyzer/cpython/_analyzer.py | 1 + 3 files changed, 7 insertions(+) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c14f925b4e7..de6d3cbce54 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3523,7 +3523,10 @@ _testcapi_exec(PyObject *m) return 0; } +PyABIInfo_VAR(abi_info); + static PyModuleDef_Slot _testcapi_slots[] = { + {Py_mod_abi, &abi_info}, {Py_mod_exec, _testcapi_exec}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 4140cd23ded..a7fbb0f87b6 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -2696,7 +2696,10 @@ module_exec(PyObject *module) return 0; } +PyABIInfo_VAR(abi_info); + static struct PyModuleDef_Slot module_slots[] = { + {Py_mod_abi, &abi_info}, {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Tools/c-analyzer/cpython/_analyzer.py b/Tools/c-analyzer/cpython/_analyzer.py index 6f0f4648928..43ed552fcf7 100644 --- a/Tools/c-analyzer/cpython/_analyzer.py +++ b/Tools/c-analyzer/cpython/_analyzer.py @@ -76,6 +76,7 @@ _OTHER_SUPPORTED_TYPES = { 'PyBufferProcs', 'PyStructSequence_Field[]', 'PyStructSequence_Desc', + 'PyABIInfo', } # XXX We should normalize all cases to a single name, From 77c8e6a2b8e206ea8151ab1b431e32f1cad51ddd Mon Sep 17 00:00:00 2001 From: Damian Birchler Date: Wed, 17 Dec 2025 17:40:03 +0100 Subject: [PATCH 22/80] gh-142876: remove reference to thread in documentation of `asyncio.Queue.shutdown` (#142888) --- Doc/library/asyncio-queue.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/asyncio-queue.rst b/Doc/library/asyncio-queue.rst index d481a1921d5..a9735ae8065 100644 --- a/Doc/library/asyncio-queue.rst +++ b/Doc/library/asyncio-queue.rst @@ -107,7 +107,7 @@ Queue The queue can no longer grow. Future calls to :meth:`~Queue.put` raise :exc:`QueueShutDown`. Currently blocked callers of :meth:`~Queue.put` will be unblocked - and will raise :exc:`QueueShutDown` in the formerly blocked thread. + and will raise :exc:`QueueShutDown` in the formerly awaiting task. If *immediate* is false (the default), the queue can be wound down normally with :meth:`~Queue.get` calls to extract tasks From e61a447d0e0753621e8fc347e99dfc87884d68ab Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 17 Dec 2025 19:41:36 +0300 Subject: [PATCH 23/80] gh-142873: Do not check for `PyContextVar_CheckExact` twice in `PyContextVar_Set` (#142874) --- Python/context.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Python/context.c b/Python/context.c index 620e78ab1f9..606ce4b1c8f 100644 --- a/Python/context.c +++ b/Python/context.c @@ -343,12 +343,6 @@ PyContextVar_Set(PyObject *ovar, PyObject *val) ENSURE_ContextVar(ovar, NULL) PyContextVar *var = (PyContextVar *)ovar; - if (!PyContextVar_CheckExact(var)) { - PyErr_SetString( - PyExc_TypeError, "an instance of ContextVar was expected"); - return NULL; - } - PyContext *ctx = context_get(); if (ctx == NULL) { return NULL; @@ -1025,12 +1019,6 @@ static PyObject * _contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value) /*[clinic end generated code: output=0746bd0aa2ced7bf input=da66664d5d0af4ad]*/ { - if (!PyContextVar_CheckExact(self)) { - PyErr_SetString( - PyExc_TypeError, "an instance of ContextVar was expected"); - return NULL; - } - PyObject *val; if (PyContextVar_Get((PyObject *)self, default_value, &val) < 0) { return NULL; From fba4584ffccde789919799c38a55ce4c17ba4d8b Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Thu, 18 Dec 2025 01:05:21 +0800 Subject: [PATCH 24/80] gh-142849: Fix segfault in `executor_to_gv` (GH-142885) Fix segfault in `executor_to_gv` --- Python/optimizer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 18f54f9bc23..fc984a5374a 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1977,7 +1977,7 @@ executor_to_gv(_PyExecutorObject *executor, FILE *out) #else fprintf(out, " %s op0=%" PRIu64 "\n", i, opname, inst->operand0); #endif - if (inst->opcode == _EXIT_TRACE || inst->opcode == _JUMP_TO_TOP) { + if (base_opcode == _EXIT_TRACE || base_opcode == _JUMP_TO_TOP) { break; } } @@ -1990,7 +1990,7 @@ executor_to_gv(_PyExecutorObject *executor, FILE *out) for (uint32_t i = 0; i < executor->code_size; i++) { _PyUOpInstruction const *inst = &executor->trace[i]; uint16_t base_opcode = _PyUop_Uncached[inst->opcode]; - uint16_t flags = _PyUop_Flags[inst->opcode]; + uint16_t flags = _PyUop_Flags[base_opcode]; _PyExitData *exit = NULL; if (base_opcode == _EXIT_TRACE) { exit = (_PyExitData *)inst->operand0; From 77b56eafde0fc3dacd4faad1b2b757c50d841d76 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Wed, 17 Dec 2025 17:43:52 +0000 Subject: [PATCH 25/80] gh-91048: Refactor common data into context object in Modules/_remote_debugging (#142879) --- Modules/_remote_debugging/_remote_debugging.h | 53 +++-- Modules/_remote_debugging/code_objects.c | 29 +-- Modules/_remote_debugging/frame_cache.c | 8 + Modules/_remote_debugging/frames.c | 220 ++++++++---------- Modules/_remote_debugging/threads.c | 26 ++- 5 files changed, 172 insertions(+), 164 deletions(-) diff --git a/Modules/_remote_debugging/_remote_debugging.h b/Modules/_remote_debugging/_remote_debugging.h index 663479f235a..cc8aeedba9c 100644 --- a/Modules/_remote_debugging/_remote_debugging.h +++ b/Modules/_remote_debugging/_remote_debugging.h @@ -279,6 +279,35 @@ typedef struct { size_t count; } StackChunkList; +/* + * Context for frame chain traversal operations. + */ +typedef struct { + /* Inputs */ + uintptr_t frame_addr; // Starting frame address + uintptr_t base_frame_addr; // Sentinel at bottom (for validation) + uintptr_t gc_frame; // GC frame address (0 if not tracking) + uintptr_t last_profiled_frame; // Last cached frame (0 if no cache) + StackChunkList *chunks; // Pre-copied stack chunks + + /* Outputs */ + PyObject *frame_info; // List to append FrameInfo objects + uintptr_t *frame_addrs; // Array of visited frame addresses + Py_ssize_t num_addrs; // Count of addresses collected + Py_ssize_t max_addrs; // Capacity of frame_addrs array + uintptr_t last_frame_visited; // Last frame address visited + int stopped_at_cached_frame; // Whether we stopped at cached frame +} FrameWalkContext; + +/* + * Context for code object parsing. + */ +typedef struct { + uintptr_t code_addr; // Code object address in remote process + uintptr_t instruction_pointer; // Current instruction pointer + int32_t tlbc_index; // Thread-local bytecode index (free-threading) +} CodeObjectContext; + /* Function pointer types for iteration callbacks */ typedef int (*thread_processor_func)( RemoteUnwinderObject *unwinder, @@ -343,10 +372,7 @@ extern long read_py_long(RemoteUnwinderObject *unwinder, uintptr_t address); extern int parse_code_object( RemoteUnwinderObject *unwinder, PyObject **result, - uintptr_t address, - uintptr_t instruction_pointer, - uintptr_t *previous_frame, - int32_t tlbc_index + const CodeObjectContext *ctx ); extern PyObject *make_location_info( @@ -420,17 +446,7 @@ extern void *find_frame_in_chunks(StackChunkList *chunks, uintptr_t remote_ptr); extern int process_frame_chain( RemoteUnwinderObject *unwinder, - uintptr_t initial_frame_addr, - StackChunkList *chunks, - PyObject *frame_info, - uintptr_t base_frame_addr, - uintptr_t gc_frame, - uintptr_t last_profiled_frame, - int *stopped_at_cached_frame, - uintptr_t *frame_addrs, - Py_ssize_t *num_addrs, - Py_ssize_t max_addrs, - uintptr_t *out_last_frame_addr + FrameWalkContext *ctx ); /* Frame cache functions */ @@ -460,12 +476,7 @@ extern int frame_cache_store( extern int collect_frames_with_cache( RemoteUnwinderObject *unwinder, - uintptr_t frame_addr, - StackChunkList *chunks, - PyObject *frame_info, - uintptr_t base_frame_addr, - uintptr_t gc_frame, - uintptr_t last_profiled_frame, + FrameWalkContext *ctx, uint64_t thread_id); /* ============================================================================ diff --git a/Modules/_remote_debugging/code_objects.c b/Modules/_remote_debugging/code_objects.c index 98fe74e8cb6..ca6ffe7a00a 100644 --- a/Modules/_remote_debugging/code_objects.c +++ b/Modules/_remote_debugging/code_objects.c @@ -76,6 +76,7 @@ cache_tlbc_array(RemoteUnwinderObject *unwinder, uintptr_t code_addr, uintptr_t PyErr_SetString(PyExc_RuntimeError, "TLBC array size exceeds maximum limit"); return 0; // Invalid size } + assert(tlbc_size > 0 && tlbc_size <= MAX_TLBC_SIZE); // Allocate and read the entire TLBC array size_t array_data_size = tlbc_size * sizeof(void*); @@ -156,8 +157,11 @@ parse_linetable(const uintptr_t addrq, const char* linetable, int firstlineno, L const uint8_t* ptr = (const uint8_t*)(linetable); uintptr_t addr = 0; int computed_line = firstlineno; // Running accumulator, separate from output + const size_t MAX_LINETABLE_ENTRIES = 65536; + size_t entry_count = 0; - while (*ptr != '\0') { + while (*ptr != '\0' && entry_count < MAX_LINETABLE_ENTRIES) { + entry_count++; uint8_t first_byte = *(ptr++); uint8_t code = (first_byte >> 3) & 15; size_t length = (first_byte & 7) + 1; @@ -277,12 +281,9 @@ make_frame_info(RemoteUnwinderObject *unwinder, PyObject *file, PyObject *locati int parse_code_object(RemoteUnwinderObject *unwinder, PyObject **result, - uintptr_t address, - uintptr_t instruction_pointer, - uintptr_t *previous_frame, - int32_t tlbc_index) + const CodeObjectContext *ctx) { - void *key = (void *)address; + void *key = (void *)ctx->code_addr; CachedCodeMetadata *meta = NULL; PyObject *func = NULL; PyObject *file = NULL; @@ -291,9 +292,9 @@ parse_code_object(RemoteUnwinderObject *unwinder, #ifdef Py_GIL_DISABLED // In free threading builds, code object addresses might have the low bit set // as a flag, so we need to mask it off to get the real address - uintptr_t real_address = address & (~1); + uintptr_t real_address = ctx->code_addr & (~1); #else - uintptr_t real_address = address; + uintptr_t real_address = ctx->code_addr; #endif if (unwinder && unwinder->code_object_cache != NULL) { @@ -360,12 +361,12 @@ parse_code_object(RemoteUnwinderObject *unwinder, linetable = NULL; } - uintptr_t ip = instruction_pointer; + uintptr_t ip = ctx->instruction_pointer; ptrdiff_t addrq; #ifdef Py_GIL_DISABLED // Handle thread-local bytecode (TLBC) in free threading builds - if (tlbc_index == 0 || unwinder->debug_offsets.code_object.co_tlbc == 0 || unwinder == NULL) { + if (ctx->tlbc_index == 0 || unwinder->debug_offsets.code_object.co_tlbc == 0 || unwinder == NULL) { // No TLBC or no unwinder - use main bytecode directly addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive; goto done_tlbc; @@ -383,10 +384,12 @@ parse_code_object(RemoteUnwinderObject *unwinder, tlbc_entry = get_tlbc_cache_entry(unwinder, real_address, unwinder->tlbc_generation); } - if (tlbc_entry && tlbc_index < tlbc_entry->tlbc_array_size) { + if (tlbc_entry && ctx->tlbc_index < tlbc_entry->tlbc_array_size) { + assert(ctx->tlbc_index >= 0); + assert(tlbc_entry->tlbc_array_size > 0); // Use cached TLBC data uintptr_t *entries = (uintptr_t *)((char *)tlbc_entry->tlbc_array + sizeof(Py_ssize_t)); - uintptr_t tlbc_bytecode_addr = entries[tlbc_index]; + uintptr_t tlbc_bytecode_addr = entries[ctx->tlbc_index]; if (tlbc_bytecode_addr != 0) { // Calculate offset from TLBC bytecode @@ -401,8 +404,6 @@ parse_code_object(RemoteUnwinderObject *unwinder, done_tlbc: #else // Non-free-threaded build, always use the main bytecode - (void)tlbc_index; // Suppress unused parameter warning - (void)unwinder; // Suppress unused parameter warning addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive; #endif ; // Empty statement to avoid C23 extension warning diff --git a/Modules/_remote_debugging/frame_cache.c b/Modules/_remote_debugging/frame_cache.c index ab7891445e0..e94f4d3d81c 100644 --- a/Modules/_remote_debugging/frame_cache.c +++ b/Modules/_remote_debugging/frame_cache.c @@ -44,7 +44,9 @@ frame_cache_find(RemoteUnwinderObject *unwinder, uint64_t thread_id) return NULL; } for (int i = 0; i < FRAME_CACHE_MAX_THREADS; i++) { + assert(i >= 0 && i < FRAME_CACHE_MAX_THREADS); if (unwinder->frame_cache[i].thread_id == thread_id) { + assert(unwinder->frame_cache[i].num_addrs <= FRAME_CACHE_MAX_FRAMES); return &unwinder->frame_cache[i]; } } @@ -154,6 +156,8 @@ frame_cache_lookup_and_extend( return 0; } + assert(entry->num_addrs >= 0 && entry->num_addrs <= FRAME_CACHE_MAX_FRAMES); + // Find the index where last_profiled_frame matches Py_ssize_t start_idx = -1; for (Py_ssize_t i = 0; i < entry->num_addrs; i++) { @@ -166,6 +170,7 @@ frame_cache_lookup_and_extend( if (start_idx < 0) { return 0; // Not found } + assert(start_idx < entry->num_addrs); Py_ssize_t num_frames = PyList_GET_SIZE(entry->frame_list); @@ -225,6 +230,7 @@ frame_cache_store( if (num_addrs > FRAME_CACHE_MAX_FRAMES) { num_addrs = FRAME_CACHE_MAX_FRAMES; } + assert(num_addrs >= 0 && num_addrs <= FRAME_CACHE_MAX_FRAMES); FrameCacheEntry *entry = frame_cache_alloc_slot(unwinder, thread_id); if (!entry) { @@ -245,6 +251,8 @@ frame_cache_store( entry->thread_id = thread_id; memcpy(entry->addrs, addrs, num_addrs * sizeof(uintptr_t)); entry->num_addrs = num_addrs; + assert(entry->num_addrs == num_addrs); + assert(entry->thread_id == thread_id); return 1; } diff --git a/Modules/_remote_debugging/frames.c b/Modules/_remote_debugging/frames.c index 47e34e9f945..8aebd40671c 100644 --- a/Modules/_remote_debugging/frames.c +++ b/Modules/_remote_debugging/frames.c @@ -88,7 +88,8 @@ copy_stack_chunks(RemoteUnwinderObject *unwinder, return -1; } - while (chunk_addr != 0) { + const size_t MAX_STACK_CHUNKS = 4096; + while (chunk_addr != 0 && count < MAX_STACK_CHUNKS) { // Grow array if needed if (count >= max_chunks) { max_chunks *= 2; @@ -128,6 +129,7 @@ void * find_frame_in_chunks(StackChunkList *chunks, uintptr_t remote_ptr) { for (size_t i = 0; i < chunks->count; ++i) { + assert(chunks->chunks[i].size > offsetof(_PyStackChunk, data)); uintptr_t base = chunks->chunks[i].remote_addr + offsetof(_PyStackChunk, data); size_t payload = chunks->chunks[i].size - offsetof(_PyStackChunk, data); @@ -209,7 +211,13 @@ parse_frame_object( #endif *address_of_code_object = code_object; - return parse_code_object(unwinder, result, code_object, instruction_pointer, previous_frame, tlbc_index); + + CodeObjectContext code_ctx = { + .code_addr = code_object, + .instruction_pointer = instruction_pointer, + .tlbc_index = tlbc_index, + }; + return parse_code_object(unwinder, result, &code_ctx); } int @@ -246,7 +254,12 @@ parse_frame_from_chunks( } #endif - return parse_code_object(unwinder, result, code_object, instruction_pointer, previous_frame, tlbc_index); + CodeObjectContext code_ctx = { + .code_addr = code_object, + .instruction_pointer = instruction_pointer, + .tlbc_index = tlbc_index, + }; + return parse_code_object(unwinder, result, &code_ctx); } /* ============================================================================ @@ -256,105 +269,80 @@ parse_frame_from_chunks( int process_frame_chain( RemoteUnwinderObject *unwinder, - uintptr_t initial_frame_addr, - StackChunkList *chunks, - PyObject *frame_info, - uintptr_t base_frame_addr, - uintptr_t gc_frame, - uintptr_t last_profiled_frame, - int *stopped_at_cached_frame, - uintptr_t *frame_addrs, // optional: C array to receive frame addresses - Py_ssize_t *num_addrs, // in/out: current count / updated count - Py_ssize_t max_addrs, // max capacity of frame_addrs array - uintptr_t *out_last_frame_addr) // optional: receives last frame address visited + FrameWalkContext *ctx) { - uintptr_t frame_addr = initial_frame_addr; + uintptr_t frame_addr = ctx->frame_addr; uintptr_t prev_frame_addr = 0; - uintptr_t last_frame_addr = 0; // Track last frame visited for validation + uintptr_t last_frame_addr = 0; const size_t MAX_FRAMES = 1024 + 512; size_t frame_count = 0; + assert(MAX_FRAMES > 0 && MAX_FRAMES < 10000); - // Initialize output parameters - if (stopped_at_cached_frame) { - *stopped_at_cached_frame = 0; - } - if (out_last_frame_addr) { - *out_last_frame_addr = 0; - } + ctx->stopped_at_cached_frame = 0; + ctx->last_frame_visited = 0; - // Quick check: if current_frame == last_profiled_frame, entire stack is unchanged - if (last_profiled_frame != 0 && initial_frame_addr == last_profiled_frame) { - if (stopped_at_cached_frame) { - *stopped_at_cached_frame = 1; - } + if (ctx->last_profiled_frame != 0 && ctx->frame_addr == ctx->last_profiled_frame) { + ctx->stopped_at_cached_frame = 1; return 0; } while ((void*)frame_addr != NULL) { - // Check if we've reached the cached frame - if so, stop here - if (last_profiled_frame != 0 && frame_addr == last_profiled_frame) { - if (stopped_at_cached_frame) { - *stopped_at_cached_frame = 1; - } + if (ctx->last_profiled_frame != 0 && frame_addr == ctx->last_profiled_frame) { + ctx->stopped_at_cached_frame = 1; break; } PyObject *frame = NULL; uintptr_t next_frame_addr = 0; uintptr_t stackpointer = 0; - last_frame_addr = frame_addr; // Remember this frame address + last_frame_addr = frame_addr; if (++frame_count > MAX_FRAMES) { PyErr_SetString(PyExc_RuntimeError, "Too many stack frames (possible infinite loop)"); set_exception_cause(unwinder, PyExc_RuntimeError, "Frame chain iteration limit exceeded"); return -1; } + assert(frame_count <= MAX_FRAMES); - if (parse_frame_from_chunks(unwinder, &frame, frame_addr, &next_frame_addr, &stackpointer, chunks) < 0) { + if (parse_frame_from_chunks(unwinder, &frame, frame_addr, &next_frame_addr, &stackpointer, ctx->chunks) < 0) { PyErr_Clear(); uintptr_t address_of_code_object = 0; - if (parse_frame_object(unwinder, &frame, frame_addr, &address_of_code_object ,&next_frame_addr) < 0) { + if (parse_frame_object(unwinder, &frame, frame_addr, &address_of_code_object, &next_frame_addr) < 0) { set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse frame object in chain"); return -1; } } - if (frame == NULL && PyList_GET_SIZE(frame_info) == 0) { + if (frame == NULL && PyList_GET_SIZE(ctx->frame_info) == 0) { const char *e = "Failed to parse initial frame in chain"; PyErr_SetString(PyExc_RuntimeError, e); return -1; } PyObject *extra_frame = NULL; - // This frame kicked off the current GC collection: - if (unwinder->gc && frame_addr == gc_frame) { + if (unwinder->gc && frame_addr == ctx->gc_frame) { _Py_DECLARE_STR(gc, ""); extra_frame = &_Py_STR(gc); } - // Otherwise, check for native frames to insert: else if (unwinder->native && - // We've reached an interpreter trampoline frame: frame == NULL && - // Bottommost frame is always native, so skip that one: next_frame_addr && - // Only suppress native frames if GC tracking is enabled and the next frame will be a GC frame: - !(unwinder->gc && next_frame_addr == gc_frame)) + !(unwinder->gc && next_frame_addr == ctx->gc_frame)) { _Py_DECLARE_STR(native, ""); extra_frame = &_Py_STR(native); } if (extra_frame) { - // Use "~" as file, None as location (synthetic frame), None as opcode PyObject *extra_frame_info = make_frame_info( unwinder, _Py_LATIN1_CHR('~'), Py_None, extra_frame, Py_None); if (extra_frame_info == NULL) { return -1; } - if (PyList_Append(frame_info, extra_frame_info) < 0) { + if (PyList_Append(ctx->frame_info, extra_frame_info) < 0) { Py_DECREF(extra_frame_info); set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append extra frame"); return -1; } - // Extra frames use 0 as address (they're synthetic) - if (frame_addrs && *num_addrs < max_addrs) { - frame_addrs[(*num_addrs)++] = 0; + if (ctx->frame_addrs && ctx->num_addrs < ctx->max_addrs) { + assert(ctx->num_addrs >= 0); + ctx->frame_addrs[ctx->num_addrs++] = 0; } Py_DECREF(extra_frame_info); } @@ -367,14 +355,14 @@ process_frame_chain( return -1; } - if (PyList_Append(frame_info, frame) < 0) { + if (PyList_Append(ctx->frame_info, frame) < 0) { Py_DECREF(frame); set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame"); return -1; } - // Track the address for this frame - if (frame_addrs && *num_addrs < max_addrs) { - frame_addrs[(*num_addrs)++] = frame_addr; + if (ctx->frame_addrs && ctx->num_addrs < ctx->max_addrs) { + assert(ctx->num_addrs >= 0); + ctx->frame_addrs[ctx->num_addrs++] = frame_addr; } Py_DECREF(frame); } @@ -383,21 +371,14 @@ process_frame_chain( frame_addr = next_frame_addr; } - // Validate we reached the base frame (sentinel at bottom of stack) - // Only validate if we walked the full chain (didn't stop at cached frame) - // and base_frame_addr is provided (non-zero) - int stopped_early = stopped_at_cached_frame && *stopped_at_cached_frame; - if (!stopped_early && base_frame_addr != 0 && last_frame_addr != base_frame_addr) { + if (!ctx->stopped_at_cached_frame && ctx->base_frame_addr != 0 && last_frame_addr != ctx->base_frame_addr) { PyErr_Format(PyExc_RuntimeError, "Incomplete sample: did not reach base frame (expected 0x%lx, got 0x%lx)", - base_frame_addr, last_frame_addr); + ctx->base_frame_addr, last_frame_addr); return -1; } - // Set output parameter for caller (needed for cache validation) - if (out_last_frame_addr) { - *out_last_frame_addr = last_frame_addr; - } + ctx->last_frame_visited = last_frame_addr; return 0; } @@ -410,8 +391,11 @@ clear_last_profiled_frames(RemoteUnwinderObject *unwinder) { uintptr_t current_interp = unwinder->interpreter_addr; uintptr_t zero = 0; + const size_t MAX_INTERPRETERS = 256; + size_t interp_count = 0; - while (current_interp != 0) { + while (current_interp != 0 && interp_count < MAX_INTERPRETERS) { + interp_count++; // Get first thread in this interpreter uintptr_t tstate_addr; if (_Py_RemoteDebug_PagedReadRemoteMemory( @@ -425,7 +409,10 @@ clear_last_profiled_frames(RemoteUnwinderObject *unwinder) } // Iterate all threads in this interpreter - while (tstate_addr != 0) { + const size_t MAX_THREADS_PER_INTERP = 8192; + size_t thread_count = 0; + while (tstate_addr != 0 && thread_count < MAX_THREADS_PER_INTERP) { + thread_count++; // Clear last_profiled_frame uintptr_t lpf_addr = tstate_addr + unwinder->debug_offsets.thread_state.last_profiled_frame; if (_Py_RemoteDebug_WriteRemoteMemory(&unwinder->handle, lpf_addr, @@ -468,16 +455,13 @@ clear_last_profiled_frames(RemoteUnwinderObject *unwinder) static int try_full_cache_hit( RemoteUnwinderObject *unwinder, - uintptr_t frame_addr, - uintptr_t last_profiled_frame, - uint64_t thread_id, - PyObject *frame_info) + const FrameWalkContext *ctx, + uint64_t thread_id) { - if (!unwinder->frame_cache || last_profiled_frame == 0) { + if (!unwinder->frame_cache || ctx->last_profiled_frame == 0) { return 0; } - // Full hit only if current frame == last profiled frame - if (frame_addr != last_profiled_frame) { + if (ctx->frame_addr != ctx->last_profiled_frame) { return 0; } @@ -486,22 +470,19 @@ try_full_cache_hit( return 0; } - // Verify first address matches (sanity check) - if (entry->num_addrs == 0 || entry->addrs[0] != frame_addr) { + if (entry->num_addrs == 0 || entry->addrs[0] != ctx->frame_addr) { return 0; } - // Always read the current frame from memory to get updated line number PyObject *current_frame = NULL; uintptr_t code_object_addr = 0; uintptr_t previous_frame = 0; - int parse_result = parse_frame_object(unwinder, ¤t_frame, frame_addr, + int parse_result = parse_frame_object(unwinder, ¤t_frame, ctx->frame_addr, &code_object_addr, &previous_frame); if (parse_result < 0) { return -1; } - // Get cached parent frames first (before modifying frame_info) Py_ssize_t cached_size = PyList_GET_SIZE(entry->frame_list); PyObject *parent_slice = NULL; if (cached_size > 1) { @@ -512,9 +493,8 @@ try_full_cache_hit( } } - // Now safe to modify frame_info - add current frame if valid if (current_frame != NULL) { - if (PyList_Append(frame_info, current_frame) < 0) { + if (PyList_Append(ctx->frame_info, current_frame) < 0) { Py_DECREF(current_frame); Py_XDECREF(parent_slice); return -1; @@ -523,10 +503,9 @@ try_full_cache_hit( STATS_ADD(unwinder, frames_read_from_memory, 1); } - // Extend with cached parent frames if (parent_slice) { - Py_ssize_t cur_size = PyList_GET_SIZE(frame_info); - int result = PyList_SetSlice(frame_info, cur_size, cur_size, parent_slice); + Py_ssize_t cur_size = PyList_GET_SIZE(ctx->frame_info); + int result = PyList_SetSlice(ctx->frame_info, cur_size, cur_size, parent_slice); Py_DECREF(parent_slice); if (result < 0) { return -1; @@ -543,72 +522,67 @@ try_full_cache_hit( int collect_frames_with_cache( RemoteUnwinderObject *unwinder, - uintptr_t frame_addr, - StackChunkList *chunks, - PyObject *frame_info, - uintptr_t base_frame_addr, - uintptr_t gc_frame, - uintptr_t last_profiled_frame, + FrameWalkContext *ctx, uint64_t thread_id) { - // Fast path: check for full cache hit first (no allocations needed) - int full_hit = try_full_cache_hit(unwinder, frame_addr, last_profiled_frame, - thread_id, frame_info); + int full_hit = try_full_cache_hit(unwinder, ctx, thread_id); if (full_hit != 0) { - return full_hit < 0 ? -1 : 0; // Either error or success + return full_hit < 0 ? -1 : 0; } - uintptr_t addrs[FRAME_CACHE_MAX_FRAMES]; - Py_ssize_t num_addrs = 0; - Py_ssize_t frames_before = PyList_GET_SIZE(frame_info); - uintptr_t last_frame_visited = 0; + Py_ssize_t frames_before = PyList_GET_SIZE(ctx->frame_info); - int stopped_at_cached = 0; - if (process_frame_chain(unwinder, frame_addr, chunks, frame_info, base_frame_addr, gc_frame, - last_profiled_frame, &stopped_at_cached, - addrs, &num_addrs, FRAME_CACHE_MAX_FRAMES, - &last_frame_visited) < 0) { + if (process_frame_chain(unwinder, ctx) < 0) { return -1; } - // Track frames read from memory (frames added by process_frame_chain) - STATS_ADD(unwinder, frames_read_from_memory, PyList_GET_SIZE(frame_info) - frames_before); + STATS_ADD(unwinder, frames_read_from_memory, PyList_GET_SIZE(ctx->frame_info) - frames_before); - // If stopped at cached frame, extend with cached continuation (both frames and addresses) - if (stopped_at_cached) { - Py_ssize_t frames_before_cache = PyList_GET_SIZE(frame_info); - int cache_result = frame_cache_lookup_and_extend(unwinder, thread_id, last_profiled_frame, - frame_info, addrs, &num_addrs, - FRAME_CACHE_MAX_FRAMES); + if (ctx->stopped_at_cached_frame) { + Py_ssize_t frames_before_cache = PyList_GET_SIZE(ctx->frame_info); + int cache_result = frame_cache_lookup_and_extend(unwinder, thread_id, ctx->last_profiled_frame, + ctx->frame_info, ctx->frame_addrs, &ctx->num_addrs, + ctx->max_addrs); if (cache_result < 0) { return -1; } if (cache_result == 0) { - // Cache miss - continue walking from last_profiled_frame to get the rest STATS_INC(unwinder, frame_cache_misses); - Py_ssize_t frames_before_walk = PyList_GET_SIZE(frame_info); - if (process_frame_chain(unwinder, last_profiled_frame, chunks, frame_info, base_frame_addr, gc_frame, - 0, NULL, addrs, &num_addrs, FRAME_CACHE_MAX_FRAMES, - &last_frame_visited) < 0) { + Py_ssize_t frames_before_walk = PyList_GET_SIZE(ctx->frame_info); + + FrameWalkContext continue_ctx = { + .frame_addr = ctx->last_profiled_frame, + .base_frame_addr = ctx->base_frame_addr, + .gc_frame = ctx->gc_frame, + .last_profiled_frame = 0, + .chunks = ctx->chunks, + .frame_info = ctx->frame_info, + .frame_addrs = ctx->frame_addrs, + .num_addrs = ctx->num_addrs, + .max_addrs = ctx->max_addrs, + }; + if (process_frame_chain(unwinder, &continue_ctx) < 0) { return -1; } - STATS_ADD(unwinder, frames_read_from_memory, PyList_GET_SIZE(frame_info) - frames_before_walk); + ctx->num_addrs = continue_ctx.num_addrs; + ctx->last_frame_visited = continue_ctx.last_frame_visited; + + STATS_ADD(unwinder, frames_read_from_memory, PyList_GET_SIZE(ctx->frame_info) - frames_before_walk); } else { - // Partial cache hit - cache was validated when stored, so we trust it + // Partial cache hit - cached stack was validated as complete when stored, + // so set last_frame_visited to base_frame_addr for validation in frame_cache_store + ctx->last_frame_visited = ctx->base_frame_addr; STATS_INC(unwinder, frame_cache_partial_hits); - STATS_ADD(unwinder, frames_read_from_cache, PyList_GET_SIZE(frame_info) - frames_before_cache); + STATS_ADD(unwinder, frames_read_from_cache, PyList_GET_SIZE(ctx->frame_info) - frames_before_cache); } } else { - if (last_profiled_frame == 0) { - // No cache involvement (no last_profiled_frame or cache disabled) + if (ctx->last_profiled_frame == 0) { STATS_INC(unwinder, frame_cache_misses); } } - // Store in cache - frame_cache_store validates internally that we have a - // complete stack (reached base_frame_addr) before actually storing - if (frame_cache_store(unwinder, thread_id, frame_info, addrs, num_addrs, - base_frame_addr, last_frame_visited) < 0) { + if (frame_cache_store(unwinder, thread_id, ctx->frame_info, ctx->frame_addrs, ctx->num_addrs, + ctx->base_frame_addr, ctx->last_frame_visited) < 0) { return -1; } diff --git a/Modules/_remote_debugging/threads.c b/Modules/_remote_debugging/threads.c index 6db774ecfc2..3a5b8adb3f4 100644 --- a/Modules/_remote_debugging/threads.c +++ b/Modules/_remote_debugging/threads.c @@ -23,6 +23,8 @@ iterate_threads( ) { uintptr_t thread_state_addr; unsigned long tid = 0; + const size_t MAX_THREADS = 8192; + size_t thread_count = 0; if (0 > _Py_RemoteDebug_PagedReadRemoteMemory( &unwinder->handle, @@ -34,7 +36,8 @@ iterate_threads( return -1; } - while (thread_state_addr != 0) { + while (thread_state_addr != 0 && thread_count < MAX_THREADS) { + thread_count++; if (0 > _Py_RemoteDebug_PagedReadRemoteMemory( &unwinder->handle, thread_state_addr + (uintptr_t)unwinder->debug_offsets.thread_state.native_thread_id, @@ -425,12 +428,24 @@ unwind_stack_for_thread( } } + uintptr_t addrs[FRAME_CACHE_MAX_FRAMES]; + FrameWalkContext ctx = { + .frame_addr = frame_addr, + .base_frame_addr = base_frame_addr, + .gc_frame = gc_frame, + .chunks = &chunks, + .frame_info = frame_info, + .frame_addrs = addrs, + .num_addrs = 0, + .max_addrs = FRAME_CACHE_MAX_FRAMES, + }; + assert(ctx.max_addrs == FRAME_CACHE_MAX_FRAMES); + if (unwinder->cache_frames) { // Use cache to avoid re-reading unchanged parent frames - uintptr_t last_profiled_frame = GET_MEMBER(uintptr_t, ts, + ctx.last_profiled_frame = GET_MEMBER(uintptr_t, ts, unwinder->debug_offsets.thread_state.last_profiled_frame); - if (collect_frames_with_cache(unwinder, frame_addr, &chunks, frame_info, - base_frame_addr, gc_frame, last_profiled_frame, tid) < 0) { + if (collect_frames_with_cache(unwinder, &ctx, tid) < 0) { set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to collect frames"); goto error; } @@ -443,8 +458,7 @@ unwind_stack_for_thread( } } else { // No caching - process entire frame chain with base_frame validation - if (process_frame_chain(unwinder, frame_addr, &chunks, frame_info, - base_frame_addr, gc_frame, 0, NULL, NULL, NULL, 0, NULL) < 0) { + if (process_frame_chain(unwinder, &ctx) < 0) { set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process frame chain"); goto error; } From 25397f9541be89264d35d41a67defcfeaa950844 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Wed, 17 Dec 2025 13:06:32 -0500 Subject: [PATCH 26/80] gh-142766: Clear frame when `generator.close()` is called (gh-142838) --- Lib/test/test_generators.py | 27 +++++++++++++++++++ ...-12-16-11-56-20.gh-issue-142766.Uy2HTm.rst | 1 + Objects/genobject.c | 1 + 3 files changed, 29 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-11-56-20.gh-issue-142766.Uy2HTm.rst diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 3e41c7b9663..358bb27b117 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -290,6 +290,33 @@ class GeneratorTest(unittest.TestCase): self.assertEqual([1,2], list(i for i in C())) + def test_close_clears_frame(self): + # gh-142766: Test that closing a generator clears its frame + class DetectDelete: + def __init__(self): + DetectDelete.deleted = False + + def __del__(self): + DetectDelete.deleted = True + + def generator(arg): + yield + + # Test a freshly created generator (not suspended) + g = generator(DetectDelete()) + g.close() + self.assertTrue(DetectDelete.deleted) + + # Test a suspended generator + g = generator(DetectDelete()) + next(g) + g.close() + self.assertTrue(DetectDelete.deleted) + + # Clear via gi_frame.clear() + g = generator(DetectDelete()) + g.gi_frame.clear() + self.assertTrue(DetectDelete.deleted) class ModifyUnderlyingIterableTest(unittest.TestCase): iterables = [ diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-11-56-20.gh-issue-142766.Uy2HTm.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-11-56-20.gh-issue-142766.Uy2HTm.rst new file mode 100644 index 00000000000..6a14976a6dc --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-16-11-56-20.gh-issue-142766.Uy2HTm.rst @@ -0,0 +1 @@ +Clear the frame of a generator when :meth:`generator.close` is called. diff --git a/Objects/genobject.c b/Objects/genobject.c index e47180a23d2..3694198289d 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -390,6 +390,7 @@ gen_close(PyObject *self, PyObject *args) if (gen->gi_frame_state == FRAME_CREATED) { gen->gi_frame_state = FRAME_COMPLETED; + gen_clear_frame(gen); Py_RETURN_NONE; } if (FRAME_STATE_FINISHED(gen->gi_frame_state)) { From 92243dc62ce10715ab0d9074b23dea5a1bfa9dcc Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 17 Dec 2025 11:21:45 -0800 Subject: [PATCH 27/80] GH-100964: Fix reference cycle in exhausted generator frames (#141112) --- Lib/test/test_generators.py | 12 ++++++++++++ .../2025-11-06-05-21-25.gh-issue-100964.TxPf1b.rst | 1 + Python/ceval.c | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-11-06-05-21-25.gh-issue-100964.TxPf1b.rst diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 358bb27b117..6643c396edf 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -134,6 +134,18 @@ class FinalizationTest(unittest.TestCase): self.assertEqual(len(resurrected), 1) self.assertIsInstance(resurrected[0].gi_code, types.CodeType) + def test_exhausted_generator_frame_cycle(self): + def g(): + yield + + generator = g() + frame = generator.gi_frame + self.assertIsNone(frame.f_back) + next(generator) + self.assertIsNone(frame.f_back) + next(generator, None) + self.assertIsNone(frame.f_back) + class GeneratorTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-06-05-21-25.gh-issue-100964.TxPf1b.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-06-05-21-25.gh-issue-100964.TxPf1b.rst new file mode 100644 index 00000000000..7c554cf8dda --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-06-05-21-25.gh-issue-100964.TxPf1b.rst @@ -0,0 +1 @@ +Fix reference cycle in exhausted generator frames. Patch by Savannah Ostrowski. diff --git a/Python/ceval.c b/Python/ceval.c index 37679d4cd18..90ae0b022e3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2309,9 +2309,9 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); + frame->previous = NULL; _PyFrame_ClearExceptCode(frame); _PyErr_ClearExcState(&gen->gi_exc_state); - frame->previous = NULL; } void From 6e625f87d262f8dbdc439bc680626d10086d667d Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Wed, 17 Dec 2025 15:27:04 -0500 Subject: [PATCH 28/80] gh-129748: Remove TSan suppression for mi_block_set_nextx (gh-142887) The function was already changed to use a relaxed atomic store in gh-134238. --- Tools/tsan/suppressions_free_threading.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index 404c3015736..46489f5cd9d 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -23,9 +23,6 @@ race_top:write_thread_id # gh-129068: race on shared range iterators (test_free_threading.test_zip.ZipThreading.test_threading) race_top:rangeiter_next -# gh-129748: test.test_free_threading.test_slots.TestSlots.test_object -race_top:mi_block_set_nextx - # https://gist.github.com/mpage/6962e8870606cfc960e159b407a0cb40 thread:pthread_create From 8b64dd853ded593e1f0c5df786f1f1cc74dc7239 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Thu, 18 Dec 2025 06:47:47 +0800 Subject: [PATCH 29/80] gh-139757: Treat call specially in JIT assembly backend optimizer on x86-64 and AArch64 (GH-142907) Co-authored-by: Savannah Ostrowski --- ...5-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst | 1 + Tools/jit/_optimizers.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst new file mode 100644 index 00000000000..3c476d3eaea --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst @@ -0,0 +1 @@ +Fix building JIT stencils on free-threaded builds. diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 08d4e700652..83c878d8fe2 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -95,6 +95,7 @@ class InstructionKind(enum.Enum): JUMP = enum.auto() LONG_BRANCH = enum.auto() SHORT_BRANCH = enum.auto() + CALL = enum.auto() RETURN = enum.auto() SMALL_CONST_1 = enum.auto() SMALL_CONST_2 = enum.auto() @@ -182,6 +183,8 @@ class Optimizer: # Two groups (instruction and target): _re_branch: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH # One group (target): + _re_call: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH + # One group (target): _re_jump: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH # No groups: _re_return: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH @@ -225,6 +228,11 @@ class Optimizer: assert inst.target is not None block.target = self._lookup_label(inst.target) assert block.fallthrough + elif inst.kind == InstructionKind.CALL: + # A block ending in a call has a target and return point after call: + assert inst.target is not None + block.target = self._lookup_label(inst.target) + assert block.fallthrough elif inst.kind == InstructionKind.JUMP: # A block ending in a jump has a target and no fallthrough: assert inst.target is not None @@ -256,6 +264,10 @@ class Optimizer: target = match["target"] name = line[: -len(target)].strip() kind = InstructionKind.JUMP + elif match := self._re_call.match(line): + target = match["target"] + name = line[: -len(target)].strip() + kind = InstructionKind.CALL elif match := self._re_return.match(line): name = line kind = InstructionKind.RETURN @@ -463,6 +475,8 @@ class Optimizer: for index, block in enumerate(self._blocks()): if block.target and block.fallthrough: branch = block.instructions[-1] + if branch.kind == InstructionKind.CALL: + continue assert branch.is_branch() target = branch.target assert target is not None @@ -566,6 +580,8 @@ class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods rf"\s*(?P{'|'.join(_branch_patterns)})\s+(.+,\s+)*(?P[\w.]+)" ) + # https://developer.arm.com/documentation/ddi0406/b/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/BL--BLX--immediate- + _re_call = re.compile(r"\s*blx?\s+(?P[\w.]+)") # https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch- _re_jump = re.compile(r"\s*b\s+(?P[\w.]+)") # https://developer.arm.com/documentation/ddi0602/2025-09/Base-Instructions/RET--Return-from-subroutine- @@ -628,6 +644,8 @@ class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods _re_branch = re.compile( rf"\s*(?P{'|'.join(_X86_BRANCHES)})\s+(?P[\w.]+)" ) + # https://www.felixcloutier.com/x86/call + _re_call = re.compile(r"\s*callq?\s+(?P[\w.]+)") # https://www.felixcloutier.com/x86/jmp _re_jump = re.compile(r"\s*jmp\s+(?P[\w.]+)") # https://www.felixcloutier.com/x86/ret From cbc0851ada9c5bc4018fb5075c82abe2ef4fc4cf Mon Sep 17 00:00:00 2001 From: ivonastojanovic <80911834+ivonastojanovic@users.noreply.github.com> Date: Thu, 18 Dec 2025 11:43:39 +0000 Subject: [PATCH 30/80] gh-138122: Improve bytecode panel (#142910) The bytecode panel appears when a user generates a heatmap with --opcodes and clicks the button to unfold a line and display the bytecode instructions. Currently, an empty space appears on the left where the line number, self, and total columns are displayed. This area should instead extend those columns, rather than leaving a gap. --- .../sampling/_heatmap_assets/heatmap.css | 78 +++++++++++++++++-- .../sampling/_heatmap_assets/heatmap.js | 11 ++- Lib/profiling/sampling/heatmap_collector.py | 12 ++- 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap.css b/Lib/profiling/sampling/_heatmap_assets/heatmap.css index 65a13d7245d..9999cd6760f 100644 --- a/Lib/profiling/sampling/_heatmap_assets/heatmap.css +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap.css @@ -1141,6 +1141,10 @@ .line-samples-cumulative { padding: 0 4px; } + + .bytecode-panel { + margin: 8px 10px 8px 160px; + } } .bytecode-toggle { @@ -1172,13 +1176,77 @@ } .bytecode-panel { - margin-left: 90px; - padding: 8px 15px; - background: var(--bg-secondary); - border-left: 3px solid var(--accent); + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 8px; + box-shadow: var(--shadow-md); font-family: var(--font-mono); font-size: 12px; - margin-bottom: 4px; + color: var(--text-primary); + line-height: 1.5; + word-wrap: break-word; + overflow-wrap: break-word; + padding: 0; + margin: 8px 10px 8px 250px; + position: relative; + z-index: 1; + overflow-y: auto; + max-height: 500px; + flex: 1; + transition: padding 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.bytecode-panel.expanded { + padding: 14px; +} + +.bytecode-wrapper { + position: relative; + display: flex; + overflow: visible; + max-height: 0; + opacity: 0; + transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease-in-out; +} + +.bytecode-wrapper.expanded { + max-height: 600px; + opacity: 1; + transition: max-height 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease-in-out; +} + +/* Column backdrop matching table header columns (line/self/total) */ +.bytecode-columns { + display: none; + position: absolute; + left: 0; + overflow: hidden; + pointer-events: none; + z-index: 0; +} + +.bytecode-wrapper.expanded .bytecode-columns { + display: flex; + top: 0; + bottom: 0; +} + +.bytecode-panel::-webkit-scrollbar { + width: 8px; +} + +.bytecode-panel::-webkit-scrollbar-track { + background: var(--bg-secondary); + border-radius: 4px; +} + +.bytecode-panel::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 4px; +} + +.bytecode-panel::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); } /* Specialization summary bar */ diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap.js b/Lib/profiling/sampling/_heatmap_assets/heatmap.js index 8ac4ef43e53..d6a91ef2903 100644 --- a/Lib/profiling/sampling/_heatmap_assets/heatmap.js +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap.js @@ -542,20 +542,23 @@ function toggleBytecode(button) { const lineId = lineDiv.id; const lineNum = lineId.replace('line-', ''); const panel = document.getElementById(`bytecode-${lineNum}`); + const wrapper = document.getElementById(`bytecode-wrapper-${lineNum}`); - if (!panel) return; + if (!panel || !wrapper) return; - const isExpanded = panel.style.display !== 'none'; + const isExpanded = panel.classList.contains('expanded'); if (isExpanded) { - panel.style.display = 'none'; + panel.classList.remove('expanded'); + wrapper.classList.remove('expanded'); button.classList.remove('expanded'); button.innerHTML = '▶'; // Right arrow } else { if (!panel.dataset.populated) { populateBytecodePanel(panel, button); } - panel.style.display = 'block'; + panel.classList.add('expanded'); + wrapper.classList.add('expanded'); button.classList.add('expanded'); button.innerHTML = '▼'; // Down arrow } diff --git a/Lib/profiling/sampling/heatmap_collector.py b/Lib/profiling/sampling/heatmap_collector.py index 5b4c89283be..e6701901aa3 100644 --- a/Lib/profiling/sampling/heatmap_collector.py +++ b/Lib/profiling/sampling/heatmap_collector.py @@ -978,7 +978,17 @@ class HeatmapCollector(StackTraceCollector): f'data-spec-pct="{spec_pct}" ' f'onclick="toggleBytecode(this)" title="Show bytecode">▶' ) - bytecode_panel_html = f' \n' + # Wrapper contains columns + content panel + bytecode_panel_html = ( + f'
\n' + f'
' + f'
' + f'
' + f'
' + f'
\n' + f'
\n' + f'
\n' + ) elif self.opcodes_enabled: # Add invisible spacer to maintain consistent indentation when opcodes are enabled bytecode_btn_html = '
' From fc80096a07a3a1da429e251d062a1cf0422cca41 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 18 Dec 2025 07:17:42 -0500 Subject: [PATCH 31/80] gh-137063: Document that ast node types replaced by Constant are no longer available (#137064) --- Doc/library/ast.rst | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 2e7d0dbc26e..bf37540e5fa 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -139,12 +139,13 @@ Node classes The :meth:`~object.__repr__` output of :class:`~ast.AST` nodes includes the values of the node fields. -.. deprecated:: 3.8 +.. deprecated-removed:: 3.8 3.14 - Old classes :class:`!ast.Num`, :class:`!ast.Str`, :class:`!ast.Bytes`, - :class:`!ast.NameConstant` and :class:`!ast.Ellipsis` are still available, - but they will be removed in future Python releases. In the meantime, - instantiating them will return an instance of a different class. + Previous versions of Python provided the AST classes :class:`!ast.Num`, + :class:`!ast.Str`, :class:`!ast.Bytes`, :class:`!ast.NameConstant` and + :class:`!ast.Ellipsis`, which were deprecated in Python 3.8. These classes + were removed in Python 3.14, and their functionality has been replaced with + :class:`ast.Constant`. .. deprecated:: 3.9 @@ -2419,12 +2420,12 @@ and classes for traversing abstract syntax trees: during traversal. For this a special visitor exists (:class:`NodeTransformer`) that allows modifications. - .. deprecated:: 3.8 + .. deprecated-removed:: 3.8 3.14 Methods :meth:`!visit_Num`, :meth:`!visit_Str`, :meth:`!visit_Bytes`, - :meth:`!visit_NameConstant` and :meth:`!visit_Ellipsis` are deprecated - now and will not be called in future Python versions. Add the - :meth:`visit_Constant` method to handle all constant nodes. + :meth:`!visit_NameConstant` and :meth:`!visit_Ellipsis` will not be called + in Python 3.14+. Add the :meth:`visit_Constant` method instead to handle + all constant nodes. .. class:: NodeTransformer() From 71a7cb8887cd2974de323e2fd8d0f1435789389c Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Thu, 18 Dec 2025 21:33:18 +0900 Subject: [PATCH 32/80] gh-134584: Remove redundant refcount from _BINARY_OP_ADD_UNICODE (gh-142825) --- Include/internal/pycore_opcode_metadata.h | 4 +- Include/internal/pycore_uop_ids.h | 1463 ++++++++++----------- Include/internal/pycore_uop_metadata.h | 26 +- Lib/test/test_capi/test_opt.py | 16 + Python/bytecodes.c | 14 +- Python/executor_cases.c.h | 89 +- Python/generated_cases.c.h | 24 +- Python/optimizer_bytecodes.c | 12 +- Python/optimizer_cases.c.h | 42 +- 9 files changed, 859 insertions(+), 831 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index a4f1f50e96d..906ea0db0a5 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1081,7 +1081,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = { [BINARY_OP] = { true, INSTR_FMT_IBC0000, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG }, [BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG }, - [BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG }, [BINARY_OP_EXTEND] = { true, INSTR_FMT_IXC0000, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC0000, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG }, @@ -1331,7 +1331,7 @@ _PyOpcode_macro_expansion[256] = { [BINARY_OP] = { .nuops = 1, .uops = { { _BINARY_OP, OPARG_SIMPLE, 4 } } }, [BINARY_OP_ADD_FLOAT] = { .nuops = 5, .uops = { { _GUARD_TOS_FLOAT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_FLOAT, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_FLOAT, OPARG_SIMPLE, 5 }, { _POP_TOP_FLOAT, OPARG_SIMPLE, 5 }, { _POP_TOP_FLOAT, OPARG_SIMPLE, 5 } } }, [BINARY_OP_ADD_INT] = { .nuops = 5, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_INT, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 } } }, - [BINARY_OP_ADD_UNICODE] = { .nuops = 3, .uops = { { _GUARD_TOS_UNICODE, OPARG_SIMPLE, 0 }, { _GUARD_NOS_UNICODE, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_UNICODE, OPARG_SIMPLE, 5 } } }, + [BINARY_OP_ADD_UNICODE] = { .nuops = 5, .uops = { { _GUARD_TOS_UNICODE, OPARG_SIMPLE, 0 }, { _GUARD_NOS_UNICODE, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_UNICODE, OPARG_SIMPLE, 5 }, { _POP_TOP_UNICODE, OPARG_SIMPLE, 5 }, { _POP_TOP_UNICODE, OPARG_SIMPLE, 5 } } }, [BINARY_OP_EXTEND] = { .nuops = 2, .uops = { { _GUARD_BINARY_OP_EXTEND, 4, 1 }, { _BINARY_OP_EXTEND, 4, 1 } } }, [BINARY_OP_INPLACE_ADD_UNICODE] = { .nuops = 3, .uops = { { _GUARD_TOS_UNICODE, OPARG_SIMPLE, 0 }, { _GUARD_NOS_UNICODE, OPARG_SIMPLE, 0 }, { _BINARY_OP_INPLACE_ADD_UNICODE, OPARG_SIMPLE, 5 } } }, [BINARY_OP_MULTIPLY_FLOAT] = { .nuops = 5, .uops = { { _GUARD_TOS_FLOAT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_FLOAT, OPARG_SIMPLE, 0 }, { _BINARY_OP_MULTIPLY_FLOAT, OPARG_SIMPLE, 5 }, { _POP_TOP_FLOAT, OPARG_SIMPLE, 5 }, { _POP_TOP_FLOAT, OPARG_SIMPLE, 5 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 8678ccf5e1b..f73aad9de1c 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -365,738 +365,737 @@ extern "C" { #define _BINARY_OP_ADD_INT_r03 558 #define _BINARY_OP_ADD_INT_r13 559 #define _BINARY_OP_ADD_INT_r23 560 -#define _BINARY_OP_ADD_UNICODE_r01 561 -#define _BINARY_OP_ADD_UNICODE_r11 562 -#define _BINARY_OP_ADD_UNICODE_r21 563 -#define _BINARY_OP_ADD_UNICODE_r32 564 -#define _BINARY_OP_EXTEND_r21 565 -#define _BINARY_OP_INPLACE_ADD_UNICODE_r20 566 -#define _BINARY_OP_MULTIPLY_FLOAT_r03 567 -#define _BINARY_OP_MULTIPLY_FLOAT_r13 568 -#define _BINARY_OP_MULTIPLY_FLOAT_r23 569 -#define _BINARY_OP_MULTIPLY_INT_r03 570 -#define _BINARY_OP_MULTIPLY_INT_r13 571 -#define _BINARY_OP_MULTIPLY_INT_r23 572 -#define _BINARY_OP_SUBSCR_CHECK_FUNC_r23 573 -#define _BINARY_OP_SUBSCR_DICT_r21 574 -#define _BINARY_OP_SUBSCR_INIT_CALL_r01 575 -#define _BINARY_OP_SUBSCR_INIT_CALL_r11 576 -#define _BINARY_OP_SUBSCR_INIT_CALL_r21 577 -#define _BINARY_OP_SUBSCR_INIT_CALL_r31 578 -#define _BINARY_OP_SUBSCR_LIST_INT_r21 579 -#define _BINARY_OP_SUBSCR_LIST_SLICE_r21 580 -#define _BINARY_OP_SUBSCR_STR_INT_r21 581 -#define _BINARY_OP_SUBSCR_TUPLE_INT_r21 582 -#define _BINARY_OP_SUBTRACT_FLOAT_r03 583 -#define _BINARY_OP_SUBTRACT_FLOAT_r13 584 -#define _BINARY_OP_SUBTRACT_FLOAT_r23 585 -#define _BINARY_OP_SUBTRACT_INT_r03 586 -#define _BINARY_OP_SUBTRACT_INT_r13 587 -#define _BINARY_OP_SUBTRACT_INT_r23 588 -#define _BINARY_SLICE_r31 589 -#define _BUILD_INTERPOLATION_r01 590 -#define _BUILD_LIST_r01 591 -#define _BUILD_MAP_r01 592 -#define _BUILD_SET_r01 593 -#define _BUILD_SLICE_r01 594 -#define _BUILD_STRING_r01 595 -#define _BUILD_TEMPLATE_r21 596 -#define _BUILD_TUPLE_r01 597 -#define _CALL_BUILTIN_CLASS_r01 598 -#define _CALL_BUILTIN_FAST_r01 599 -#define _CALL_BUILTIN_FAST_WITH_KEYWORDS_r01 600 -#define _CALL_BUILTIN_O_r03 601 -#define _CALL_INTRINSIC_1_r11 602 -#define _CALL_INTRINSIC_2_r21 603 -#define _CALL_ISINSTANCE_r31 604 -#define _CALL_KW_NON_PY_r11 605 -#define _CALL_LEN_r33 606 -#define _CALL_LIST_APPEND_r02 607 -#define _CALL_LIST_APPEND_r12 608 -#define _CALL_LIST_APPEND_r22 609 -#define _CALL_LIST_APPEND_r32 610 -#define _CALL_METHOD_DESCRIPTOR_FAST_r01 611 -#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01 612 -#define _CALL_METHOD_DESCRIPTOR_NOARGS_r01 613 -#define _CALL_METHOD_DESCRIPTOR_O_r01 614 -#define _CALL_NON_PY_GENERAL_r01 615 -#define _CALL_STR_1_r32 616 -#define _CALL_TUPLE_1_r32 617 -#define _CALL_TYPE_1_r31 618 -#define _CHECK_AND_ALLOCATE_OBJECT_r00 619 -#define _CHECK_ATTR_CLASS_r01 620 -#define _CHECK_ATTR_CLASS_r11 621 -#define _CHECK_ATTR_CLASS_r22 622 -#define _CHECK_ATTR_CLASS_r33 623 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r01 624 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r11 625 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r22 626 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r33 627 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS_r00 628 -#define _CHECK_EG_MATCH_r22 629 -#define _CHECK_EXC_MATCH_r22 630 -#define _CHECK_FUNCTION_EXACT_ARGS_r00 631 -#define _CHECK_FUNCTION_VERSION_r00 632 -#define _CHECK_FUNCTION_VERSION_INLINE_r00 633 -#define _CHECK_FUNCTION_VERSION_INLINE_r11 634 -#define _CHECK_FUNCTION_VERSION_INLINE_r22 635 -#define _CHECK_FUNCTION_VERSION_INLINE_r33 636 -#define _CHECK_FUNCTION_VERSION_KW_r11 637 -#define _CHECK_IS_NOT_PY_CALLABLE_r00 638 -#define _CHECK_IS_NOT_PY_CALLABLE_KW_r11 639 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r01 640 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r11 641 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r22 642 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r33 643 -#define _CHECK_METHOD_VERSION_r00 644 -#define _CHECK_METHOD_VERSION_KW_r11 645 -#define _CHECK_PEP_523_r00 646 -#define _CHECK_PEP_523_r11 647 -#define _CHECK_PEP_523_r22 648 -#define _CHECK_PEP_523_r33 649 -#define _CHECK_PERIODIC_r00 650 -#define _CHECK_PERIODIC_AT_END_r00 651 -#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM_r00 652 -#define _CHECK_RECURSION_REMAINING_r00 653 -#define _CHECK_RECURSION_REMAINING_r11 654 -#define _CHECK_RECURSION_REMAINING_r22 655 -#define _CHECK_RECURSION_REMAINING_r33 656 -#define _CHECK_STACK_SPACE_r00 657 -#define _CHECK_STACK_SPACE_OPERAND_r00 658 -#define _CHECK_STACK_SPACE_OPERAND_r11 659 -#define _CHECK_STACK_SPACE_OPERAND_r22 660 -#define _CHECK_STACK_SPACE_OPERAND_r33 661 -#define _CHECK_VALIDITY_r00 662 -#define _CHECK_VALIDITY_r11 663 -#define _CHECK_VALIDITY_r22 664 -#define _CHECK_VALIDITY_r33 665 -#define _COLD_DYNAMIC_EXIT_r00 666 -#define _COLD_EXIT_r00 667 -#define _COMPARE_OP_r21 668 -#define _COMPARE_OP_FLOAT_r01 669 -#define _COMPARE_OP_FLOAT_r11 670 -#define _COMPARE_OP_FLOAT_r21 671 -#define _COMPARE_OP_FLOAT_r32 672 -#define _COMPARE_OP_INT_r23 673 -#define _COMPARE_OP_STR_r21 674 -#define _CONTAINS_OP_r21 675 -#define _CONTAINS_OP_DICT_r21 676 -#define _CONTAINS_OP_SET_r21 677 -#define _CONVERT_VALUE_r11 678 -#define _COPY_r01 679 -#define _COPY_1_r02 680 -#define _COPY_1_r12 681 -#define _COPY_1_r23 682 -#define _COPY_2_r03 683 -#define _COPY_2_r13 684 -#define _COPY_2_r23 685 -#define _COPY_3_r03 686 -#define _COPY_3_r13 687 -#define _COPY_3_r23 688 -#define _COPY_3_r33 689 -#define _COPY_FREE_VARS_r00 690 -#define _COPY_FREE_VARS_r11 691 -#define _COPY_FREE_VARS_r22 692 -#define _COPY_FREE_VARS_r33 693 -#define _CREATE_INIT_FRAME_r01 694 -#define _DELETE_ATTR_r10 695 -#define _DELETE_DEREF_r00 696 -#define _DELETE_FAST_r00 697 -#define _DELETE_GLOBAL_r00 698 -#define _DELETE_NAME_r00 699 -#define _DELETE_SUBSCR_r20 700 -#define _DEOPT_r00 701 -#define _DEOPT_r10 702 -#define _DEOPT_r20 703 -#define _DEOPT_r30 704 -#define _DICT_MERGE_r10 705 -#define _DICT_UPDATE_r10 706 -#define _DO_CALL_r01 707 -#define _DO_CALL_FUNCTION_EX_r31 708 -#define _DO_CALL_KW_r11 709 -#define _DYNAMIC_EXIT_r00 710 -#define _DYNAMIC_EXIT_r10 711 -#define _DYNAMIC_EXIT_r20 712 -#define _DYNAMIC_EXIT_r30 713 -#define _END_FOR_r10 714 -#define _END_SEND_r21 715 -#define _ERROR_POP_N_r00 716 -#define _EXIT_INIT_CHECK_r10 717 -#define _EXIT_TRACE_r00 718 -#define _EXIT_TRACE_r10 719 -#define _EXIT_TRACE_r20 720 -#define _EXIT_TRACE_r30 721 -#define _EXPAND_METHOD_r00 722 -#define _EXPAND_METHOD_KW_r11 723 -#define _FATAL_ERROR_r00 724 -#define _FATAL_ERROR_r11 725 -#define _FATAL_ERROR_r22 726 -#define _FATAL_ERROR_r33 727 -#define _FORMAT_SIMPLE_r11 728 -#define _FORMAT_WITH_SPEC_r21 729 -#define _FOR_ITER_r23 730 -#define _FOR_ITER_GEN_FRAME_r23 731 -#define _FOR_ITER_TIER_TWO_r23 732 -#define _GET_AITER_r11 733 -#define _GET_ANEXT_r12 734 -#define _GET_AWAITABLE_r11 735 -#define _GET_ITER_r12 736 -#define _GET_LEN_r12 737 -#define _GET_YIELD_FROM_ITER_r11 738 -#define _GUARD_BINARY_OP_EXTEND_r22 739 -#define _GUARD_CALLABLE_ISINSTANCE_r03 740 -#define _GUARD_CALLABLE_ISINSTANCE_r13 741 -#define _GUARD_CALLABLE_ISINSTANCE_r23 742 -#define _GUARD_CALLABLE_ISINSTANCE_r33 743 -#define _GUARD_CALLABLE_LEN_r03 744 -#define _GUARD_CALLABLE_LEN_r13 745 -#define _GUARD_CALLABLE_LEN_r23 746 -#define _GUARD_CALLABLE_LEN_r33 747 -#define _GUARD_CALLABLE_LIST_APPEND_r03 748 -#define _GUARD_CALLABLE_LIST_APPEND_r13 749 -#define _GUARD_CALLABLE_LIST_APPEND_r23 750 -#define _GUARD_CALLABLE_LIST_APPEND_r33 751 -#define _GUARD_CALLABLE_STR_1_r03 752 -#define _GUARD_CALLABLE_STR_1_r13 753 -#define _GUARD_CALLABLE_STR_1_r23 754 -#define _GUARD_CALLABLE_STR_1_r33 755 -#define _GUARD_CALLABLE_TUPLE_1_r03 756 -#define _GUARD_CALLABLE_TUPLE_1_r13 757 -#define _GUARD_CALLABLE_TUPLE_1_r23 758 -#define _GUARD_CALLABLE_TUPLE_1_r33 759 -#define _GUARD_CALLABLE_TYPE_1_r03 760 -#define _GUARD_CALLABLE_TYPE_1_r13 761 -#define _GUARD_CALLABLE_TYPE_1_r23 762 -#define _GUARD_CALLABLE_TYPE_1_r33 763 -#define _GUARD_DORV_NO_DICT_r01 764 -#define _GUARD_DORV_NO_DICT_r11 765 -#define _GUARD_DORV_NO_DICT_r22 766 -#define _GUARD_DORV_NO_DICT_r33 767 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r01 768 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11 769 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22 770 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33 771 -#define _GUARD_GLOBALS_VERSION_r00 772 -#define _GUARD_GLOBALS_VERSION_r11 773 -#define _GUARD_GLOBALS_VERSION_r22 774 -#define _GUARD_GLOBALS_VERSION_r33 775 -#define _GUARD_IP_RETURN_GENERATOR_r00 776 -#define _GUARD_IP_RETURN_GENERATOR_r11 777 -#define _GUARD_IP_RETURN_GENERATOR_r22 778 -#define _GUARD_IP_RETURN_GENERATOR_r33 779 -#define _GUARD_IP_RETURN_VALUE_r00 780 -#define _GUARD_IP_RETURN_VALUE_r11 781 -#define _GUARD_IP_RETURN_VALUE_r22 782 -#define _GUARD_IP_RETURN_VALUE_r33 783 -#define _GUARD_IP_YIELD_VALUE_r00 784 -#define _GUARD_IP_YIELD_VALUE_r11 785 -#define _GUARD_IP_YIELD_VALUE_r22 786 -#define _GUARD_IP_YIELD_VALUE_r33 787 -#define _GUARD_IP__PUSH_FRAME_r00 788 -#define _GUARD_IP__PUSH_FRAME_r11 789 -#define _GUARD_IP__PUSH_FRAME_r22 790 -#define _GUARD_IP__PUSH_FRAME_r33 791 -#define _GUARD_IS_FALSE_POP_r00 792 -#define _GUARD_IS_FALSE_POP_r10 793 -#define _GUARD_IS_FALSE_POP_r21 794 -#define _GUARD_IS_FALSE_POP_r32 795 -#define _GUARD_IS_NONE_POP_r00 796 -#define _GUARD_IS_NONE_POP_r10 797 -#define _GUARD_IS_NONE_POP_r21 798 -#define _GUARD_IS_NONE_POP_r32 799 -#define _GUARD_IS_NOT_NONE_POP_r10 800 -#define _GUARD_IS_TRUE_POP_r00 801 -#define _GUARD_IS_TRUE_POP_r10 802 -#define _GUARD_IS_TRUE_POP_r21 803 -#define _GUARD_IS_TRUE_POP_r32 804 -#define _GUARD_KEYS_VERSION_r01 805 -#define _GUARD_KEYS_VERSION_r11 806 -#define _GUARD_KEYS_VERSION_r22 807 -#define _GUARD_KEYS_VERSION_r33 808 -#define _GUARD_NOS_DICT_r02 809 -#define _GUARD_NOS_DICT_r12 810 -#define _GUARD_NOS_DICT_r22 811 -#define _GUARD_NOS_DICT_r33 812 -#define _GUARD_NOS_FLOAT_r02 813 -#define _GUARD_NOS_FLOAT_r12 814 -#define _GUARD_NOS_FLOAT_r22 815 -#define _GUARD_NOS_FLOAT_r33 816 -#define _GUARD_NOS_INT_r02 817 -#define _GUARD_NOS_INT_r12 818 -#define _GUARD_NOS_INT_r22 819 -#define _GUARD_NOS_INT_r33 820 -#define _GUARD_NOS_LIST_r02 821 -#define _GUARD_NOS_LIST_r12 822 -#define _GUARD_NOS_LIST_r22 823 -#define _GUARD_NOS_LIST_r33 824 -#define _GUARD_NOS_NOT_NULL_r02 825 -#define _GUARD_NOS_NOT_NULL_r12 826 -#define _GUARD_NOS_NOT_NULL_r22 827 -#define _GUARD_NOS_NOT_NULL_r33 828 -#define _GUARD_NOS_NULL_r02 829 -#define _GUARD_NOS_NULL_r12 830 -#define _GUARD_NOS_NULL_r22 831 -#define _GUARD_NOS_NULL_r33 832 -#define _GUARD_NOS_OVERFLOWED_r02 833 -#define _GUARD_NOS_OVERFLOWED_r12 834 -#define _GUARD_NOS_OVERFLOWED_r22 835 -#define _GUARD_NOS_OVERFLOWED_r33 836 -#define _GUARD_NOS_TUPLE_r02 837 -#define _GUARD_NOS_TUPLE_r12 838 -#define _GUARD_NOS_TUPLE_r22 839 -#define _GUARD_NOS_TUPLE_r33 840 -#define _GUARD_NOS_UNICODE_r02 841 -#define _GUARD_NOS_UNICODE_r12 842 -#define _GUARD_NOS_UNICODE_r22 843 -#define _GUARD_NOS_UNICODE_r33 844 -#define _GUARD_NOT_EXHAUSTED_LIST_r02 845 -#define _GUARD_NOT_EXHAUSTED_LIST_r12 846 -#define _GUARD_NOT_EXHAUSTED_LIST_r22 847 -#define _GUARD_NOT_EXHAUSTED_LIST_r33 848 -#define _GUARD_NOT_EXHAUSTED_RANGE_r02 849 -#define _GUARD_NOT_EXHAUSTED_RANGE_r12 850 -#define _GUARD_NOT_EXHAUSTED_RANGE_r22 851 -#define _GUARD_NOT_EXHAUSTED_RANGE_r33 852 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r02 853 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r12 854 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r22 855 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r33 856 -#define _GUARD_THIRD_NULL_r03 857 -#define _GUARD_THIRD_NULL_r13 858 -#define _GUARD_THIRD_NULL_r23 859 -#define _GUARD_THIRD_NULL_r33 860 -#define _GUARD_TOS_ANY_SET_r01 861 -#define _GUARD_TOS_ANY_SET_r11 862 -#define _GUARD_TOS_ANY_SET_r22 863 -#define _GUARD_TOS_ANY_SET_r33 864 -#define _GUARD_TOS_DICT_r01 865 -#define _GUARD_TOS_DICT_r11 866 -#define _GUARD_TOS_DICT_r22 867 -#define _GUARD_TOS_DICT_r33 868 -#define _GUARD_TOS_FLOAT_r01 869 -#define _GUARD_TOS_FLOAT_r11 870 -#define _GUARD_TOS_FLOAT_r22 871 -#define _GUARD_TOS_FLOAT_r33 872 -#define _GUARD_TOS_INT_r01 873 -#define _GUARD_TOS_INT_r11 874 -#define _GUARD_TOS_INT_r22 875 -#define _GUARD_TOS_INT_r33 876 -#define _GUARD_TOS_LIST_r01 877 -#define _GUARD_TOS_LIST_r11 878 -#define _GUARD_TOS_LIST_r22 879 -#define _GUARD_TOS_LIST_r33 880 -#define _GUARD_TOS_OVERFLOWED_r01 881 -#define _GUARD_TOS_OVERFLOWED_r11 882 -#define _GUARD_TOS_OVERFLOWED_r22 883 -#define _GUARD_TOS_OVERFLOWED_r33 884 -#define _GUARD_TOS_SLICE_r01 885 -#define _GUARD_TOS_SLICE_r11 886 -#define _GUARD_TOS_SLICE_r22 887 -#define _GUARD_TOS_SLICE_r33 888 -#define _GUARD_TOS_TUPLE_r01 889 -#define _GUARD_TOS_TUPLE_r11 890 -#define _GUARD_TOS_TUPLE_r22 891 -#define _GUARD_TOS_TUPLE_r33 892 -#define _GUARD_TOS_UNICODE_r01 893 -#define _GUARD_TOS_UNICODE_r11 894 -#define _GUARD_TOS_UNICODE_r22 895 -#define _GUARD_TOS_UNICODE_r33 896 -#define _GUARD_TYPE_VERSION_r01 897 -#define _GUARD_TYPE_VERSION_r11 898 -#define _GUARD_TYPE_VERSION_r22 899 -#define _GUARD_TYPE_VERSION_r33 900 -#define _GUARD_TYPE_VERSION_AND_LOCK_r01 901 -#define _GUARD_TYPE_VERSION_AND_LOCK_r11 902 -#define _GUARD_TYPE_VERSION_AND_LOCK_r22 903 -#define _GUARD_TYPE_VERSION_AND_LOCK_r33 904 -#define _HANDLE_PENDING_AND_DEOPT_r00 905 -#define _HANDLE_PENDING_AND_DEOPT_r10 906 -#define _HANDLE_PENDING_AND_DEOPT_r20 907 -#define _HANDLE_PENDING_AND_DEOPT_r30 908 -#define _IMPORT_FROM_r12 909 -#define _IMPORT_NAME_r21 910 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS_r00 911 -#define _INIT_CALL_PY_EXACT_ARGS_r01 912 -#define _INIT_CALL_PY_EXACT_ARGS_0_r01 913 -#define _INIT_CALL_PY_EXACT_ARGS_1_r01 914 -#define _INIT_CALL_PY_EXACT_ARGS_2_r01 915 -#define _INIT_CALL_PY_EXACT_ARGS_3_r01 916 -#define _INIT_CALL_PY_EXACT_ARGS_4_r01 917 -#define _INSERT_NULL_r10 918 -#define _INSTRUMENTED_FOR_ITER_r23 919 -#define _INSTRUMENTED_INSTRUCTION_r00 920 -#define _INSTRUMENTED_JUMP_FORWARD_r00 921 -#define _INSTRUMENTED_JUMP_FORWARD_r11 922 -#define _INSTRUMENTED_JUMP_FORWARD_r22 923 -#define _INSTRUMENTED_JUMP_FORWARD_r33 924 -#define _INSTRUMENTED_LINE_r00 925 -#define _INSTRUMENTED_NOT_TAKEN_r00 926 -#define _INSTRUMENTED_NOT_TAKEN_r11 927 -#define _INSTRUMENTED_NOT_TAKEN_r22 928 -#define _INSTRUMENTED_NOT_TAKEN_r33 929 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r00 930 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r10 931 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r21 932 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r32 933 -#define _INSTRUMENTED_POP_JUMP_IF_NONE_r10 934 -#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE_r10 935 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r00 936 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r10 937 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r21 938 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r32 939 -#define _IS_NONE_r11 940 -#define _IS_OP_r21 941 -#define _ITER_CHECK_LIST_r02 942 -#define _ITER_CHECK_LIST_r12 943 -#define _ITER_CHECK_LIST_r22 944 -#define _ITER_CHECK_LIST_r33 945 -#define _ITER_CHECK_RANGE_r02 946 -#define _ITER_CHECK_RANGE_r12 947 -#define _ITER_CHECK_RANGE_r22 948 -#define _ITER_CHECK_RANGE_r33 949 -#define _ITER_CHECK_TUPLE_r02 950 -#define _ITER_CHECK_TUPLE_r12 951 -#define _ITER_CHECK_TUPLE_r22 952 -#define _ITER_CHECK_TUPLE_r33 953 -#define _ITER_JUMP_LIST_r02 954 -#define _ITER_JUMP_LIST_r12 955 -#define _ITER_JUMP_LIST_r22 956 -#define _ITER_JUMP_LIST_r33 957 -#define _ITER_JUMP_RANGE_r02 958 -#define _ITER_JUMP_RANGE_r12 959 -#define _ITER_JUMP_RANGE_r22 960 -#define _ITER_JUMP_RANGE_r33 961 -#define _ITER_JUMP_TUPLE_r02 962 -#define _ITER_JUMP_TUPLE_r12 963 -#define _ITER_JUMP_TUPLE_r22 964 -#define _ITER_JUMP_TUPLE_r33 965 -#define _ITER_NEXT_LIST_r23 966 -#define _ITER_NEXT_LIST_TIER_TWO_r23 967 -#define _ITER_NEXT_RANGE_r03 968 -#define _ITER_NEXT_RANGE_r13 969 -#define _ITER_NEXT_RANGE_r23 970 -#define _ITER_NEXT_TUPLE_r03 971 -#define _ITER_NEXT_TUPLE_r13 972 -#define _ITER_NEXT_TUPLE_r23 973 -#define _JUMP_BACKWARD_NO_INTERRUPT_r00 974 -#define _JUMP_BACKWARD_NO_INTERRUPT_r11 975 -#define _JUMP_BACKWARD_NO_INTERRUPT_r22 976 -#define _JUMP_BACKWARD_NO_INTERRUPT_r33 977 -#define _JUMP_TO_TOP_r00 978 -#define _LIST_APPEND_r10 979 -#define _LIST_EXTEND_r10 980 -#define _LOAD_ATTR_r10 981 -#define _LOAD_ATTR_CLASS_r11 982 -#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_r11 983 -#define _LOAD_ATTR_INSTANCE_VALUE_r02 984 -#define _LOAD_ATTR_INSTANCE_VALUE_r12 985 -#define _LOAD_ATTR_INSTANCE_VALUE_r23 986 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 987 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 988 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 989 -#define _LOAD_ATTR_METHOD_NO_DICT_r02 990 -#define _LOAD_ATTR_METHOD_NO_DICT_r12 991 -#define _LOAD_ATTR_METHOD_NO_DICT_r23 992 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 993 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 994 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 995 -#define _LOAD_ATTR_MODULE_r11 996 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 997 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 998 -#define _LOAD_ATTR_PROPERTY_FRAME_r11 999 -#define _LOAD_ATTR_SLOT_r11 1000 -#define _LOAD_ATTR_WITH_HINT_r11 1001 -#define _LOAD_BUILD_CLASS_r01 1002 -#define _LOAD_BYTECODE_r00 1003 -#define _LOAD_COMMON_CONSTANT_r01 1004 -#define _LOAD_COMMON_CONSTANT_r12 1005 -#define _LOAD_COMMON_CONSTANT_r23 1006 -#define _LOAD_CONST_r01 1007 -#define _LOAD_CONST_r12 1008 -#define _LOAD_CONST_r23 1009 -#define _LOAD_CONST_INLINE_r01 1010 -#define _LOAD_CONST_INLINE_r12 1011 -#define _LOAD_CONST_INLINE_r23 1012 -#define _LOAD_CONST_INLINE_BORROW_r01 1013 -#define _LOAD_CONST_INLINE_BORROW_r12 1014 -#define _LOAD_CONST_INLINE_BORROW_r23 1015 -#define _LOAD_CONST_UNDER_INLINE_r02 1016 -#define _LOAD_CONST_UNDER_INLINE_r12 1017 -#define _LOAD_CONST_UNDER_INLINE_r23 1018 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1019 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1020 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1021 -#define _LOAD_DEREF_r01 1022 -#define _LOAD_FAST_r01 1023 -#define _LOAD_FAST_r12 1024 -#define _LOAD_FAST_r23 1025 -#define _LOAD_FAST_0_r01 1026 -#define _LOAD_FAST_0_r12 1027 -#define _LOAD_FAST_0_r23 1028 -#define _LOAD_FAST_1_r01 1029 -#define _LOAD_FAST_1_r12 1030 -#define _LOAD_FAST_1_r23 1031 -#define _LOAD_FAST_2_r01 1032 -#define _LOAD_FAST_2_r12 1033 -#define _LOAD_FAST_2_r23 1034 -#define _LOAD_FAST_3_r01 1035 -#define _LOAD_FAST_3_r12 1036 -#define _LOAD_FAST_3_r23 1037 -#define _LOAD_FAST_4_r01 1038 -#define _LOAD_FAST_4_r12 1039 -#define _LOAD_FAST_4_r23 1040 -#define _LOAD_FAST_5_r01 1041 -#define _LOAD_FAST_5_r12 1042 -#define _LOAD_FAST_5_r23 1043 -#define _LOAD_FAST_6_r01 1044 -#define _LOAD_FAST_6_r12 1045 -#define _LOAD_FAST_6_r23 1046 -#define _LOAD_FAST_7_r01 1047 -#define _LOAD_FAST_7_r12 1048 -#define _LOAD_FAST_7_r23 1049 -#define _LOAD_FAST_AND_CLEAR_r01 1050 -#define _LOAD_FAST_AND_CLEAR_r12 1051 -#define _LOAD_FAST_AND_CLEAR_r23 1052 -#define _LOAD_FAST_BORROW_r01 1053 -#define _LOAD_FAST_BORROW_r12 1054 -#define _LOAD_FAST_BORROW_r23 1055 -#define _LOAD_FAST_BORROW_0_r01 1056 -#define _LOAD_FAST_BORROW_0_r12 1057 -#define _LOAD_FAST_BORROW_0_r23 1058 -#define _LOAD_FAST_BORROW_1_r01 1059 -#define _LOAD_FAST_BORROW_1_r12 1060 -#define _LOAD_FAST_BORROW_1_r23 1061 -#define _LOAD_FAST_BORROW_2_r01 1062 -#define _LOAD_FAST_BORROW_2_r12 1063 -#define _LOAD_FAST_BORROW_2_r23 1064 -#define _LOAD_FAST_BORROW_3_r01 1065 -#define _LOAD_FAST_BORROW_3_r12 1066 -#define _LOAD_FAST_BORROW_3_r23 1067 -#define _LOAD_FAST_BORROW_4_r01 1068 -#define _LOAD_FAST_BORROW_4_r12 1069 -#define _LOAD_FAST_BORROW_4_r23 1070 -#define _LOAD_FAST_BORROW_5_r01 1071 -#define _LOAD_FAST_BORROW_5_r12 1072 -#define _LOAD_FAST_BORROW_5_r23 1073 -#define _LOAD_FAST_BORROW_6_r01 1074 -#define _LOAD_FAST_BORROW_6_r12 1075 -#define _LOAD_FAST_BORROW_6_r23 1076 -#define _LOAD_FAST_BORROW_7_r01 1077 -#define _LOAD_FAST_BORROW_7_r12 1078 -#define _LOAD_FAST_BORROW_7_r23 1079 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1080 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1081 -#define _LOAD_FAST_CHECK_r01 1082 -#define _LOAD_FAST_CHECK_r12 1083 -#define _LOAD_FAST_CHECK_r23 1084 -#define _LOAD_FAST_LOAD_FAST_r02 1085 -#define _LOAD_FAST_LOAD_FAST_r13 1086 -#define _LOAD_FROM_DICT_OR_DEREF_r11 1087 -#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1088 -#define _LOAD_GLOBAL_r00 1089 -#define _LOAD_GLOBAL_BUILTINS_r01 1090 -#define _LOAD_GLOBAL_MODULE_r01 1091 -#define _LOAD_LOCALS_r01 1092 -#define _LOAD_LOCALS_r12 1093 -#define _LOAD_LOCALS_r23 1094 -#define _LOAD_NAME_r01 1095 -#define _LOAD_SMALL_INT_r01 1096 -#define _LOAD_SMALL_INT_r12 1097 -#define _LOAD_SMALL_INT_r23 1098 -#define _LOAD_SMALL_INT_0_r01 1099 -#define _LOAD_SMALL_INT_0_r12 1100 -#define _LOAD_SMALL_INT_0_r23 1101 -#define _LOAD_SMALL_INT_1_r01 1102 -#define _LOAD_SMALL_INT_1_r12 1103 -#define _LOAD_SMALL_INT_1_r23 1104 -#define _LOAD_SMALL_INT_2_r01 1105 -#define _LOAD_SMALL_INT_2_r12 1106 -#define _LOAD_SMALL_INT_2_r23 1107 -#define _LOAD_SMALL_INT_3_r01 1108 -#define _LOAD_SMALL_INT_3_r12 1109 -#define _LOAD_SMALL_INT_3_r23 1110 -#define _LOAD_SPECIAL_r00 1111 -#define _LOAD_SUPER_ATTR_ATTR_r31 1112 -#define _LOAD_SUPER_ATTR_METHOD_r32 1113 -#define _MAKE_CALLARGS_A_TUPLE_r33 1114 -#define _MAKE_CELL_r00 1115 -#define _MAKE_FUNCTION_r11 1116 -#define _MAKE_WARM_r00 1117 -#define _MAKE_WARM_r11 1118 -#define _MAKE_WARM_r22 1119 -#define _MAKE_WARM_r33 1120 -#define _MAP_ADD_r20 1121 -#define _MATCH_CLASS_r31 1122 -#define _MATCH_KEYS_r23 1123 -#define _MATCH_MAPPING_r02 1124 -#define _MATCH_MAPPING_r12 1125 -#define _MATCH_MAPPING_r23 1126 -#define _MATCH_SEQUENCE_r02 1127 -#define _MATCH_SEQUENCE_r12 1128 -#define _MATCH_SEQUENCE_r23 1129 -#define _MAYBE_EXPAND_METHOD_r00 1130 -#define _MAYBE_EXPAND_METHOD_KW_r11 1131 -#define _MONITOR_CALL_r00 1132 -#define _MONITOR_CALL_KW_r11 1133 -#define _MONITOR_JUMP_BACKWARD_r00 1134 -#define _MONITOR_JUMP_BACKWARD_r11 1135 -#define _MONITOR_JUMP_BACKWARD_r22 1136 -#define _MONITOR_JUMP_BACKWARD_r33 1137 -#define _MONITOR_RESUME_r00 1138 -#define _NOP_r00 1139 -#define _NOP_r11 1140 -#define _NOP_r22 1141 -#define _NOP_r33 1142 -#define _POP_CALL_r20 1143 -#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1144 -#define _POP_CALL_ONE_r30 1145 -#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1146 -#define _POP_CALL_TWO_r30 1147 -#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1148 -#define _POP_EXCEPT_r10 1149 -#define _POP_ITER_r20 1150 -#define _POP_JUMP_IF_FALSE_r00 1151 -#define _POP_JUMP_IF_FALSE_r10 1152 -#define _POP_JUMP_IF_FALSE_r21 1153 -#define _POP_JUMP_IF_FALSE_r32 1154 -#define _POP_JUMP_IF_TRUE_r00 1155 -#define _POP_JUMP_IF_TRUE_r10 1156 -#define _POP_JUMP_IF_TRUE_r21 1157 -#define _POP_JUMP_IF_TRUE_r32 1158 -#define _POP_TOP_r10 1159 -#define _POP_TOP_FLOAT_r00 1160 -#define _POP_TOP_FLOAT_r10 1161 -#define _POP_TOP_FLOAT_r21 1162 -#define _POP_TOP_FLOAT_r32 1163 -#define _POP_TOP_INT_r00 1164 -#define _POP_TOP_INT_r10 1165 -#define _POP_TOP_INT_r21 1166 -#define _POP_TOP_INT_r32 1167 -#define _POP_TOP_LOAD_CONST_INLINE_r11 1168 -#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1169 -#define _POP_TOP_NOP_r00 1170 -#define _POP_TOP_NOP_r10 1171 -#define _POP_TOP_NOP_r21 1172 -#define _POP_TOP_NOP_r32 1173 -#define _POP_TOP_UNICODE_r00 1174 -#define _POP_TOP_UNICODE_r10 1175 -#define _POP_TOP_UNICODE_r21 1176 -#define _POP_TOP_UNICODE_r32 1177 -#define _POP_TWO_r20 1178 -#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1179 -#define _PUSH_EXC_INFO_r02 1180 -#define _PUSH_EXC_INFO_r12 1181 -#define _PUSH_EXC_INFO_r23 1182 -#define _PUSH_FRAME_r10 1183 -#define _PUSH_NULL_r01 1184 -#define _PUSH_NULL_r12 1185 -#define _PUSH_NULL_r23 1186 -#define _PUSH_NULL_CONDITIONAL_r00 1187 -#define _PY_FRAME_GENERAL_r01 1188 -#define _PY_FRAME_KW_r11 1189 -#define _QUICKEN_RESUME_r00 1190 -#define _QUICKEN_RESUME_r11 1191 -#define _QUICKEN_RESUME_r22 1192 -#define _QUICKEN_RESUME_r33 1193 -#define _REPLACE_WITH_TRUE_r11 1194 -#define _RESUME_CHECK_r00 1195 -#define _RESUME_CHECK_r11 1196 -#define _RESUME_CHECK_r22 1197 -#define _RESUME_CHECK_r33 1198 -#define _RETURN_GENERATOR_r01 1199 -#define _RETURN_VALUE_r11 1200 -#define _SAVE_RETURN_OFFSET_r00 1201 -#define _SAVE_RETURN_OFFSET_r11 1202 -#define _SAVE_RETURN_OFFSET_r22 1203 -#define _SAVE_RETURN_OFFSET_r33 1204 -#define _SEND_r22 1205 -#define _SEND_GEN_FRAME_r22 1206 -#define _SETUP_ANNOTATIONS_r00 1207 -#define _SET_ADD_r10 1208 -#define _SET_FUNCTION_ATTRIBUTE_r01 1209 -#define _SET_FUNCTION_ATTRIBUTE_r11 1210 -#define _SET_FUNCTION_ATTRIBUTE_r21 1211 -#define _SET_FUNCTION_ATTRIBUTE_r32 1212 -#define _SET_IP_r00 1213 -#define _SET_IP_r11 1214 -#define _SET_IP_r22 1215 -#define _SET_IP_r33 1216 -#define _SET_UPDATE_r10 1217 -#define _SPILL_OR_RELOAD_r01 1218 -#define _SPILL_OR_RELOAD_r02 1219 -#define _SPILL_OR_RELOAD_r03 1220 -#define _SPILL_OR_RELOAD_r10 1221 -#define _SPILL_OR_RELOAD_r12 1222 -#define _SPILL_OR_RELOAD_r13 1223 -#define _SPILL_OR_RELOAD_r20 1224 -#define _SPILL_OR_RELOAD_r21 1225 -#define _SPILL_OR_RELOAD_r23 1226 -#define _SPILL_OR_RELOAD_r30 1227 -#define _SPILL_OR_RELOAD_r31 1228 -#define _SPILL_OR_RELOAD_r32 1229 -#define _START_EXECUTOR_r00 1230 -#define _STORE_ATTR_r20 1231 -#define _STORE_ATTR_INSTANCE_VALUE_r21 1232 -#define _STORE_ATTR_SLOT_r21 1233 -#define _STORE_ATTR_WITH_HINT_r21 1234 -#define _STORE_DEREF_r10 1235 -#define _STORE_FAST_r10 1236 -#define _STORE_FAST_0_r10 1237 -#define _STORE_FAST_1_r10 1238 -#define _STORE_FAST_2_r10 1239 -#define _STORE_FAST_3_r10 1240 -#define _STORE_FAST_4_r10 1241 -#define _STORE_FAST_5_r10 1242 -#define _STORE_FAST_6_r10 1243 -#define _STORE_FAST_7_r10 1244 -#define _STORE_FAST_LOAD_FAST_r11 1245 -#define _STORE_FAST_STORE_FAST_r20 1246 -#define _STORE_GLOBAL_r10 1247 -#define _STORE_NAME_r10 1248 -#define _STORE_SLICE_r30 1249 -#define _STORE_SUBSCR_r30 1250 -#define _STORE_SUBSCR_DICT_r31 1251 -#define _STORE_SUBSCR_LIST_INT_r32 1252 -#define _SWAP_r11 1253 -#define _SWAP_2_r02 1254 -#define _SWAP_2_r12 1255 -#define _SWAP_2_r22 1256 -#define _SWAP_2_r33 1257 -#define _SWAP_3_r03 1258 -#define _SWAP_3_r13 1259 -#define _SWAP_3_r23 1260 -#define _SWAP_3_r33 1261 -#define _TIER2_RESUME_CHECK_r00 1262 -#define _TIER2_RESUME_CHECK_r11 1263 -#define _TIER2_RESUME_CHECK_r22 1264 -#define _TIER2_RESUME_CHECK_r33 1265 -#define _TO_BOOL_r11 1266 -#define _TO_BOOL_BOOL_r01 1267 -#define _TO_BOOL_BOOL_r11 1268 -#define _TO_BOOL_BOOL_r22 1269 -#define _TO_BOOL_BOOL_r33 1270 -#define _TO_BOOL_INT_r11 1271 -#define _TO_BOOL_LIST_r11 1272 -#define _TO_BOOL_NONE_r01 1273 -#define _TO_BOOL_NONE_r11 1274 -#define _TO_BOOL_NONE_r22 1275 -#define _TO_BOOL_NONE_r33 1276 -#define _TO_BOOL_STR_r11 1277 -#define _TRACE_RECORD_r00 1278 -#define _UNARY_INVERT_r11 1279 -#define _UNARY_NEGATIVE_r11 1280 -#define _UNARY_NOT_r01 1281 -#define _UNARY_NOT_r11 1282 -#define _UNARY_NOT_r22 1283 -#define _UNARY_NOT_r33 1284 -#define _UNPACK_EX_r10 1285 -#define _UNPACK_SEQUENCE_r10 1286 -#define _UNPACK_SEQUENCE_LIST_r10 1287 -#define _UNPACK_SEQUENCE_TUPLE_r10 1288 -#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1289 -#define _WITH_EXCEPT_START_r33 1290 -#define _YIELD_VALUE_r11 1291 -#define MAX_UOP_REGS_ID 1291 +#define _BINARY_OP_ADD_UNICODE_r03 561 +#define _BINARY_OP_ADD_UNICODE_r13 562 +#define _BINARY_OP_ADD_UNICODE_r23 563 +#define _BINARY_OP_EXTEND_r21 564 +#define _BINARY_OP_INPLACE_ADD_UNICODE_r20 565 +#define _BINARY_OP_MULTIPLY_FLOAT_r03 566 +#define _BINARY_OP_MULTIPLY_FLOAT_r13 567 +#define _BINARY_OP_MULTIPLY_FLOAT_r23 568 +#define _BINARY_OP_MULTIPLY_INT_r03 569 +#define _BINARY_OP_MULTIPLY_INT_r13 570 +#define _BINARY_OP_MULTIPLY_INT_r23 571 +#define _BINARY_OP_SUBSCR_CHECK_FUNC_r23 572 +#define _BINARY_OP_SUBSCR_DICT_r21 573 +#define _BINARY_OP_SUBSCR_INIT_CALL_r01 574 +#define _BINARY_OP_SUBSCR_INIT_CALL_r11 575 +#define _BINARY_OP_SUBSCR_INIT_CALL_r21 576 +#define _BINARY_OP_SUBSCR_INIT_CALL_r31 577 +#define _BINARY_OP_SUBSCR_LIST_INT_r21 578 +#define _BINARY_OP_SUBSCR_LIST_SLICE_r21 579 +#define _BINARY_OP_SUBSCR_STR_INT_r21 580 +#define _BINARY_OP_SUBSCR_TUPLE_INT_r21 581 +#define _BINARY_OP_SUBTRACT_FLOAT_r03 582 +#define _BINARY_OP_SUBTRACT_FLOAT_r13 583 +#define _BINARY_OP_SUBTRACT_FLOAT_r23 584 +#define _BINARY_OP_SUBTRACT_INT_r03 585 +#define _BINARY_OP_SUBTRACT_INT_r13 586 +#define _BINARY_OP_SUBTRACT_INT_r23 587 +#define _BINARY_SLICE_r31 588 +#define _BUILD_INTERPOLATION_r01 589 +#define _BUILD_LIST_r01 590 +#define _BUILD_MAP_r01 591 +#define _BUILD_SET_r01 592 +#define _BUILD_SLICE_r01 593 +#define _BUILD_STRING_r01 594 +#define _BUILD_TEMPLATE_r21 595 +#define _BUILD_TUPLE_r01 596 +#define _CALL_BUILTIN_CLASS_r01 597 +#define _CALL_BUILTIN_FAST_r01 598 +#define _CALL_BUILTIN_FAST_WITH_KEYWORDS_r01 599 +#define _CALL_BUILTIN_O_r03 600 +#define _CALL_INTRINSIC_1_r11 601 +#define _CALL_INTRINSIC_2_r21 602 +#define _CALL_ISINSTANCE_r31 603 +#define _CALL_KW_NON_PY_r11 604 +#define _CALL_LEN_r33 605 +#define _CALL_LIST_APPEND_r02 606 +#define _CALL_LIST_APPEND_r12 607 +#define _CALL_LIST_APPEND_r22 608 +#define _CALL_LIST_APPEND_r32 609 +#define _CALL_METHOD_DESCRIPTOR_FAST_r01 610 +#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01 611 +#define _CALL_METHOD_DESCRIPTOR_NOARGS_r01 612 +#define _CALL_METHOD_DESCRIPTOR_O_r01 613 +#define _CALL_NON_PY_GENERAL_r01 614 +#define _CALL_STR_1_r32 615 +#define _CALL_TUPLE_1_r32 616 +#define _CALL_TYPE_1_r31 617 +#define _CHECK_AND_ALLOCATE_OBJECT_r00 618 +#define _CHECK_ATTR_CLASS_r01 619 +#define _CHECK_ATTR_CLASS_r11 620 +#define _CHECK_ATTR_CLASS_r22 621 +#define _CHECK_ATTR_CLASS_r33 622 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r01 623 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r11 624 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r22 625 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r33 626 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS_r00 627 +#define _CHECK_EG_MATCH_r22 628 +#define _CHECK_EXC_MATCH_r22 629 +#define _CHECK_FUNCTION_EXACT_ARGS_r00 630 +#define _CHECK_FUNCTION_VERSION_r00 631 +#define _CHECK_FUNCTION_VERSION_INLINE_r00 632 +#define _CHECK_FUNCTION_VERSION_INLINE_r11 633 +#define _CHECK_FUNCTION_VERSION_INLINE_r22 634 +#define _CHECK_FUNCTION_VERSION_INLINE_r33 635 +#define _CHECK_FUNCTION_VERSION_KW_r11 636 +#define _CHECK_IS_NOT_PY_CALLABLE_r00 637 +#define _CHECK_IS_NOT_PY_CALLABLE_KW_r11 638 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r01 639 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r11 640 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r22 641 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r33 642 +#define _CHECK_METHOD_VERSION_r00 643 +#define _CHECK_METHOD_VERSION_KW_r11 644 +#define _CHECK_PEP_523_r00 645 +#define _CHECK_PEP_523_r11 646 +#define _CHECK_PEP_523_r22 647 +#define _CHECK_PEP_523_r33 648 +#define _CHECK_PERIODIC_r00 649 +#define _CHECK_PERIODIC_AT_END_r00 650 +#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM_r00 651 +#define _CHECK_RECURSION_REMAINING_r00 652 +#define _CHECK_RECURSION_REMAINING_r11 653 +#define _CHECK_RECURSION_REMAINING_r22 654 +#define _CHECK_RECURSION_REMAINING_r33 655 +#define _CHECK_STACK_SPACE_r00 656 +#define _CHECK_STACK_SPACE_OPERAND_r00 657 +#define _CHECK_STACK_SPACE_OPERAND_r11 658 +#define _CHECK_STACK_SPACE_OPERAND_r22 659 +#define _CHECK_STACK_SPACE_OPERAND_r33 660 +#define _CHECK_VALIDITY_r00 661 +#define _CHECK_VALIDITY_r11 662 +#define _CHECK_VALIDITY_r22 663 +#define _CHECK_VALIDITY_r33 664 +#define _COLD_DYNAMIC_EXIT_r00 665 +#define _COLD_EXIT_r00 666 +#define _COMPARE_OP_r21 667 +#define _COMPARE_OP_FLOAT_r01 668 +#define _COMPARE_OP_FLOAT_r11 669 +#define _COMPARE_OP_FLOAT_r21 670 +#define _COMPARE_OP_FLOAT_r32 671 +#define _COMPARE_OP_INT_r23 672 +#define _COMPARE_OP_STR_r21 673 +#define _CONTAINS_OP_r21 674 +#define _CONTAINS_OP_DICT_r21 675 +#define _CONTAINS_OP_SET_r21 676 +#define _CONVERT_VALUE_r11 677 +#define _COPY_r01 678 +#define _COPY_1_r02 679 +#define _COPY_1_r12 680 +#define _COPY_1_r23 681 +#define _COPY_2_r03 682 +#define _COPY_2_r13 683 +#define _COPY_2_r23 684 +#define _COPY_3_r03 685 +#define _COPY_3_r13 686 +#define _COPY_3_r23 687 +#define _COPY_3_r33 688 +#define _COPY_FREE_VARS_r00 689 +#define _COPY_FREE_VARS_r11 690 +#define _COPY_FREE_VARS_r22 691 +#define _COPY_FREE_VARS_r33 692 +#define _CREATE_INIT_FRAME_r01 693 +#define _DELETE_ATTR_r10 694 +#define _DELETE_DEREF_r00 695 +#define _DELETE_FAST_r00 696 +#define _DELETE_GLOBAL_r00 697 +#define _DELETE_NAME_r00 698 +#define _DELETE_SUBSCR_r20 699 +#define _DEOPT_r00 700 +#define _DEOPT_r10 701 +#define _DEOPT_r20 702 +#define _DEOPT_r30 703 +#define _DICT_MERGE_r10 704 +#define _DICT_UPDATE_r10 705 +#define _DO_CALL_r01 706 +#define _DO_CALL_FUNCTION_EX_r31 707 +#define _DO_CALL_KW_r11 708 +#define _DYNAMIC_EXIT_r00 709 +#define _DYNAMIC_EXIT_r10 710 +#define _DYNAMIC_EXIT_r20 711 +#define _DYNAMIC_EXIT_r30 712 +#define _END_FOR_r10 713 +#define _END_SEND_r21 714 +#define _ERROR_POP_N_r00 715 +#define _EXIT_INIT_CHECK_r10 716 +#define _EXIT_TRACE_r00 717 +#define _EXIT_TRACE_r10 718 +#define _EXIT_TRACE_r20 719 +#define _EXIT_TRACE_r30 720 +#define _EXPAND_METHOD_r00 721 +#define _EXPAND_METHOD_KW_r11 722 +#define _FATAL_ERROR_r00 723 +#define _FATAL_ERROR_r11 724 +#define _FATAL_ERROR_r22 725 +#define _FATAL_ERROR_r33 726 +#define _FORMAT_SIMPLE_r11 727 +#define _FORMAT_WITH_SPEC_r21 728 +#define _FOR_ITER_r23 729 +#define _FOR_ITER_GEN_FRAME_r23 730 +#define _FOR_ITER_TIER_TWO_r23 731 +#define _GET_AITER_r11 732 +#define _GET_ANEXT_r12 733 +#define _GET_AWAITABLE_r11 734 +#define _GET_ITER_r12 735 +#define _GET_LEN_r12 736 +#define _GET_YIELD_FROM_ITER_r11 737 +#define _GUARD_BINARY_OP_EXTEND_r22 738 +#define _GUARD_CALLABLE_ISINSTANCE_r03 739 +#define _GUARD_CALLABLE_ISINSTANCE_r13 740 +#define _GUARD_CALLABLE_ISINSTANCE_r23 741 +#define _GUARD_CALLABLE_ISINSTANCE_r33 742 +#define _GUARD_CALLABLE_LEN_r03 743 +#define _GUARD_CALLABLE_LEN_r13 744 +#define _GUARD_CALLABLE_LEN_r23 745 +#define _GUARD_CALLABLE_LEN_r33 746 +#define _GUARD_CALLABLE_LIST_APPEND_r03 747 +#define _GUARD_CALLABLE_LIST_APPEND_r13 748 +#define _GUARD_CALLABLE_LIST_APPEND_r23 749 +#define _GUARD_CALLABLE_LIST_APPEND_r33 750 +#define _GUARD_CALLABLE_STR_1_r03 751 +#define _GUARD_CALLABLE_STR_1_r13 752 +#define _GUARD_CALLABLE_STR_1_r23 753 +#define _GUARD_CALLABLE_STR_1_r33 754 +#define _GUARD_CALLABLE_TUPLE_1_r03 755 +#define _GUARD_CALLABLE_TUPLE_1_r13 756 +#define _GUARD_CALLABLE_TUPLE_1_r23 757 +#define _GUARD_CALLABLE_TUPLE_1_r33 758 +#define _GUARD_CALLABLE_TYPE_1_r03 759 +#define _GUARD_CALLABLE_TYPE_1_r13 760 +#define _GUARD_CALLABLE_TYPE_1_r23 761 +#define _GUARD_CALLABLE_TYPE_1_r33 762 +#define _GUARD_DORV_NO_DICT_r01 763 +#define _GUARD_DORV_NO_DICT_r11 764 +#define _GUARD_DORV_NO_DICT_r22 765 +#define _GUARD_DORV_NO_DICT_r33 766 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r01 767 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11 768 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22 769 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33 770 +#define _GUARD_GLOBALS_VERSION_r00 771 +#define _GUARD_GLOBALS_VERSION_r11 772 +#define _GUARD_GLOBALS_VERSION_r22 773 +#define _GUARD_GLOBALS_VERSION_r33 774 +#define _GUARD_IP_RETURN_GENERATOR_r00 775 +#define _GUARD_IP_RETURN_GENERATOR_r11 776 +#define _GUARD_IP_RETURN_GENERATOR_r22 777 +#define _GUARD_IP_RETURN_GENERATOR_r33 778 +#define _GUARD_IP_RETURN_VALUE_r00 779 +#define _GUARD_IP_RETURN_VALUE_r11 780 +#define _GUARD_IP_RETURN_VALUE_r22 781 +#define _GUARD_IP_RETURN_VALUE_r33 782 +#define _GUARD_IP_YIELD_VALUE_r00 783 +#define _GUARD_IP_YIELD_VALUE_r11 784 +#define _GUARD_IP_YIELD_VALUE_r22 785 +#define _GUARD_IP_YIELD_VALUE_r33 786 +#define _GUARD_IP__PUSH_FRAME_r00 787 +#define _GUARD_IP__PUSH_FRAME_r11 788 +#define _GUARD_IP__PUSH_FRAME_r22 789 +#define _GUARD_IP__PUSH_FRAME_r33 790 +#define _GUARD_IS_FALSE_POP_r00 791 +#define _GUARD_IS_FALSE_POP_r10 792 +#define _GUARD_IS_FALSE_POP_r21 793 +#define _GUARD_IS_FALSE_POP_r32 794 +#define _GUARD_IS_NONE_POP_r00 795 +#define _GUARD_IS_NONE_POP_r10 796 +#define _GUARD_IS_NONE_POP_r21 797 +#define _GUARD_IS_NONE_POP_r32 798 +#define _GUARD_IS_NOT_NONE_POP_r10 799 +#define _GUARD_IS_TRUE_POP_r00 800 +#define _GUARD_IS_TRUE_POP_r10 801 +#define _GUARD_IS_TRUE_POP_r21 802 +#define _GUARD_IS_TRUE_POP_r32 803 +#define _GUARD_KEYS_VERSION_r01 804 +#define _GUARD_KEYS_VERSION_r11 805 +#define _GUARD_KEYS_VERSION_r22 806 +#define _GUARD_KEYS_VERSION_r33 807 +#define _GUARD_NOS_DICT_r02 808 +#define _GUARD_NOS_DICT_r12 809 +#define _GUARD_NOS_DICT_r22 810 +#define _GUARD_NOS_DICT_r33 811 +#define _GUARD_NOS_FLOAT_r02 812 +#define _GUARD_NOS_FLOAT_r12 813 +#define _GUARD_NOS_FLOAT_r22 814 +#define _GUARD_NOS_FLOAT_r33 815 +#define _GUARD_NOS_INT_r02 816 +#define _GUARD_NOS_INT_r12 817 +#define _GUARD_NOS_INT_r22 818 +#define _GUARD_NOS_INT_r33 819 +#define _GUARD_NOS_LIST_r02 820 +#define _GUARD_NOS_LIST_r12 821 +#define _GUARD_NOS_LIST_r22 822 +#define _GUARD_NOS_LIST_r33 823 +#define _GUARD_NOS_NOT_NULL_r02 824 +#define _GUARD_NOS_NOT_NULL_r12 825 +#define _GUARD_NOS_NOT_NULL_r22 826 +#define _GUARD_NOS_NOT_NULL_r33 827 +#define _GUARD_NOS_NULL_r02 828 +#define _GUARD_NOS_NULL_r12 829 +#define _GUARD_NOS_NULL_r22 830 +#define _GUARD_NOS_NULL_r33 831 +#define _GUARD_NOS_OVERFLOWED_r02 832 +#define _GUARD_NOS_OVERFLOWED_r12 833 +#define _GUARD_NOS_OVERFLOWED_r22 834 +#define _GUARD_NOS_OVERFLOWED_r33 835 +#define _GUARD_NOS_TUPLE_r02 836 +#define _GUARD_NOS_TUPLE_r12 837 +#define _GUARD_NOS_TUPLE_r22 838 +#define _GUARD_NOS_TUPLE_r33 839 +#define _GUARD_NOS_UNICODE_r02 840 +#define _GUARD_NOS_UNICODE_r12 841 +#define _GUARD_NOS_UNICODE_r22 842 +#define _GUARD_NOS_UNICODE_r33 843 +#define _GUARD_NOT_EXHAUSTED_LIST_r02 844 +#define _GUARD_NOT_EXHAUSTED_LIST_r12 845 +#define _GUARD_NOT_EXHAUSTED_LIST_r22 846 +#define _GUARD_NOT_EXHAUSTED_LIST_r33 847 +#define _GUARD_NOT_EXHAUSTED_RANGE_r02 848 +#define _GUARD_NOT_EXHAUSTED_RANGE_r12 849 +#define _GUARD_NOT_EXHAUSTED_RANGE_r22 850 +#define _GUARD_NOT_EXHAUSTED_RANGE_r33 851 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r02 852 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r12 853 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r22 854 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r33 855 +#define _GUARD_THIRD_NULL_r03 856 +#define _GUARD_THIRD_NULL_r13 857 +#define _GUARD_THIRD_NULL_r23 858 +#define _GUARD_THIRD_NULL_r33 859 +#define _GUARD_TOS_ANY_SET_r01 860 +#define _GUARD_TOS_ANY_SET_r11 861 +#define _GUARD_TOS_ANY_SET_r22 862 +#define _GUARD_TOS_ANY_SET_r33 863 +#define _GUARD_TOS_DICT_r01 864 +#define _GUARD_TOS_DICT_r11 865 +#define _GUARD_TOS_DICT_r22 866 +#define _GUARD_TOS_DICT_r33 867 +#define _GUARD_TOS_FLOAT_r01 868 +#define _GUARD_TOS_FLOAT_r11 869 +#define _GUARD_TOS_FLOAT_r22 870 +#define _GUARD_TOS_FLOAT_r33 871 +#define _GUARD_TOS_INT_r01 872 +#define _GUARD_TOS_INT_r11 873 +#define _GUARD_TOS_INT_r22 874 +#define _GUARD_TOS_INT_r33 875 +#define _GUARD_TOS_LIST_r01 876 +#define _GUARD_TOS_LIST_r11 877 +#define _GUARD_TOS_LIST_r22 878 +#define _GUARD_TOS_LIST_r33 879 +#define _GUARD_TOS_OVERFLOWED_r01 880 +#define _GUARD_TOS_OVERFLOWED_r11 881 +#define _GUARD_TOS_OVERFLOWED_r22 882 +#define _GUARD_TOS_OVERFLOWED_r33 883 +#define _GUARD_TOS_SLICE_r01 884 +#define _GUARD_TOS_SLICE_r11 885 +#define _GUARD_TOS_SLICE_r22 886 +#define _GUARD_TOS_SLICE_r33 887 +#define _GUARD_TOS_TUPLE_r01 888 +#define _GUARD_TOS_TUPLE_r11 889 +#define _GUARD_TOS_TUPLE_r22 890 +#define _GUARD_TOS_TUPLE_r33 891 +#define _GUARD_TOS_UNICODE_r01 892 +#define _GUARD_TOS_UNICODE_r11 893 +#define _GUARD_TOS_UNICODE_r22 894 +#define _GUARD_TOS_UNICODE_r33 895 +#define _GUARD_TYPE_VERSION_r01 896 +#define _GUARD_TYPE_VERSION_r11 897 +#define _GUARD_TYPE_VERSION_r22 898 +#define _GUARD_TYPE_VERSION_r33 899 +#define _GUARD_TYPE_VERSION_AND_LOCK_r01 900 +#define _GUARD_TYPE_VERSION_AND_LOCK_r11 901 +#define _GUARD_TYPE_VERSION_AND_LOCK_r22 902 +#define _GUARD_TYPE_VERSION_AND_LOCK_r33 903 +#define _HANDLE_PENDING_AND_DEOPT_r00 904 +#define _HANDLE_PENDING_AND_DEOPT_r10 905 +#define _HANDLE_PENDING_AND_DEOPT_r20 906 +#define _HANDLE_PENDING_AND_DEOPT_r30 907 +#define _IMPORT_FROM_r12 908 +#define _IMPORT_NAME_r21 909 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS_r00 910 +#define _INIT_CALL_PY_EXACT_ARGS_r01 911 +#define _INIT_CALL_PY_EXACT_ARGS_0_r01 912 +#define _INIT_CALL_PY_EXACT_ARGS_1_r01 913 +#define _INIT_CALL_PY_EXACT_ARGS_2_r01 914 +#define _INIT_CALL_PY_EXACT_ARGS_3_r01 915 +#define _INIT_CALL_PY_EXACT_ARGS_4_r01 916 +#define _INSERT_NULL_r10 917 +#define _INSTRUMENTED_FOR_ITER_r23 918 +#define _INSTRUMENTED_INSTRUCTION_r00 919 +#define _INSTRUMENTED_JUMP_FORWARD_r00 920 +#define _INSTRUMENTED_JUMP_FORWARD_r11 921 +#define _INSTRUMENTED_JUMP_FORWARD_r22 922 +#define _INSTRUMENTED_JUMP_FORWARD_r33 923 +#define _INSTRUMENTED_LINE_r00 924 +#define _INSTRUMENTED_NOT_TAKEN_r00 925 +#define _INSTRUMENTED_NOT_TAKEN_r11 926 +#define _INSTRUMENTED_NOT_TAKEN_r22 927 +#define _INSTRUMENTED_NOT_TAKEN_r33 928 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r00 929 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r10 930 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r21 931 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r32 932 +#define _INSTRUMENTED_POP_JUMP_IF_NONE_r10 933 +#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE_r10 934 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r00 935 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r10 936 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r21 937 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r32 938 +#define _IS_NONE_r11 939 +#define _IS_OP_r21 940 +#define _ITER_CHECK_LIST_r02 941 +#define _ITER_CHECK_LIST_r12 942 +#define _ITER_CHECK_LIST_r22 943 +#define _ITER_CHECK_LIST_r33 944 +#define _ITER_CHECK_RANGE_r02 945 +#define _ITER_CHECK_RANGE_r12 946 +#define _ITER_CHECK_RANGE_r22 947 +#define _ITER_CHECK_RANGE_r33 948 +#define _ITER_CHECK_TUPLE_r02 949 +#define _ITER_CHECK_TUPLE_r12 950 +#define _ITER_CHECK_TUPLE_r22 951 +#define _ITER_CHECK_TUPLE_r33 952 +#define _ITER_JUMP_LIST_r02 953 +#define _ITER_JUMP_LIST_r12 954 +#define _ITER_JUMP_LIST_r22 955 +#define _ITER_JUMP_LIST_r33 956 +#define _ITER_JUMP_RANGE_r02 957 +#define _ITER_JUMP_RANGE_r12 958 +#define _ITER_JUMP_RANGE_r22 959 +#define _ITER_JUMP_RANGE_r33 960 +#define _ITER_JUMP_TUPLE_r02 961 +#define _ITER_JUMP_TUPLE_r12 962 +#define _ITER_JUMP_TUPLE_r22 963 +#define _ITER_JUMP_TUPLE_r33 964 +#define _ITER_NEXT_LIST_r23 965 +#define _ITER_NEXT_LIST_TIER_TWO_r23 966 +#define _ITER_NEXT_RANGE_r03 967 +#define _ITER_NEXT_RANGE_r13 968 +#define _ITER_NEXT_RANGE_r23 969 +#define _ITER_NEXT_TUPLE_r03 970 +#define _ITER_NEXT_TUPLE_r13 971 +#define _ITER_NEXT_TUPLE_r23 972 +#define _JUMP_BACKWARD_NO_INTERRUPT_r00 973 +#define _JUMP_BACKWARD_NO_INTERRUPT_r11 974 +#define _JUMP_BACKWARD_NO_INTERRUPT_r22 975 +#define _JUMP_BACKWARD_NO_INTERRUPT_r33 976 +#define _JUMP_TO_TOP_r00 977 +#define _LIST_APPEND_r10 978 +#define _LIST_EXTEND_r10 979 +#define _LOAD_ATTR_r10 980 +#define _LOAD_ATTR_CLASS_r11 981 +#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_r11 982 +#define _LOAD_ATTR_INSTANCE_VALUE_r02 983 +#define _LOAD_ATTR_INSTANCE_VALUE_r12 984 +#define _LOAD_ATTR_INSTANCE_VALUE_r23 985 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 986 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 987 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 988 +#define _LOAD_ATTR_METHOD_NO_DICT_r02 989 +#define _LOAD_ATTR_METHOD_NO_DICT_r12 990 +#define _LOAD_ATTR_METHOD_NO_DICT_r23 991 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 992 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 993 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 994 +#define _LOAD_ATTR_MODULE_r11 995 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 996 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 997 +#define _LOAD_ATTR_PROPERTY_FRAME_r11 998 +#define _LOAD_ATTR_SLOT_r11 999 +#define _LOAD_ATTR_WITH_HINT_r11 1000 +#define _LOAD_BUILD_CLASS_r01 1001 +#define _LOAD_BYTECODE_r00 1002 +#define _LOAD_COMMON_CONSTANT_r01 1003 +#define _LOAD_COMMON_CONSTANT_r12 1004 +#define _LOAD_COMMON_CONSTANT_r23 1005 +#define _LOAD_CONST_r01 1006 +#define _LOAD_CONST_r12 1007 +#define _LOAD_CONST_r23 1008 +#define _LOAD_CONST_INLINE_r01 1009 +#define _LOAD_CONST_INLINE_r12 1010 +#define _LOAD_CONST_INLINE_r23 1011 +#define _LOAD_CONST_INLINE_BORROW_r01 1012 +#define _LOAD_CONST_INLINE_BORROW_r12 1013 +#define _LOAD_CONST_INLINE_BORROW_r23 1014 +#define _LOAD_CONST_UNDER_INLINE_r02 1015 +#define _LOAD_CONST_UNDER_INLINE_r12 1016 +#define _LOAD_CONST_UNDER_INLINE_r23 1017 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1018 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1019 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1020 +#define _LOAD_DEREF_r01 1021 +#define _LOAD_FAST_r01 1022 +#define _LOAD_FAST_r12 1023 +#define _LOAD_FAST_r23 1024 +#define _LOAD_FAST_0_r01 1025 +#define _LOAD_FAST_0_r12 1026 +#define _LOAD_FAST_0_r23 1027 +#define _LOAD_FAST_1_r01 1028 +#define _LOAD_FAST_1_r12 1029 +#define _LOAD_FAST_1_r23 1030 +#define _LOAD_FAST_2_r01 1031 +#define _LOAD_FAST_2_r12 1032 +#define _LOAD_FAST_2_r23 1033 +#define _LOAD_FAST_3_r01 1034 +#define _LOAD_FAST_3_r12 1035 +#define _LOAD_FAST_3_r23 1036 +#define _LOAD_FAST_4_r01 1037 +#define _LOAD_FAST_4_r12 1038 +#define _LOAD_FAST_4_r23 1039 +#define _LOAD_FAST_5_r01 1040 +#define _LOAD_FAST_5_r12 1041 +#define _LOAD_FAST_5_r23 1042 +#define _LOAD_FAST_6_r01 1043 +#define _LOAD_FAST_6_r12 1044 +#define _LOAD_FAST_6_r23 1045 +#define _LOAD_FAST_7_r01 1046 +#define _LOAD_FAST_7_r12 1047 +#define _LOAD_FAST_7_r23 1048 +#define _LOAD_FAST_AND_CLEAR_r01 1049 +#define _LOAD_FAST_AND_CLEAR_r12 1050 +#define _LOAD_FAST_AND_CLEAR_r23 1051 +#define _LOAD_FAST_BORROW_r01 1052 +#define _LOAD_FAST_BORROW_r12 1053 +#define _LOAD_FAST_BORROW_r23 1054 +#define _LOAD_FAST_BORROW_0_r01 1055 +#define _LOAD_FAST_BORROW_0_r12 1056 +#define _LOAD_FAST_BORROW_0_r23 1057 +#define _LOAD_FAST_BORROW_1_r01 1058 +#define _LOAD_FAST_BORROW_1_r12 1059 +#define _LOAD_FAST_BORROW_1_r23 1060 +#define _LOAD_FAST_BORROW_2_r01 1061 +#define _LOAD_FAST_BORROW_2_r12 1062 +#define _LOAD_FAST_BORROW_2_r23 1063 +#define _LOAD_FAST_BORROW_3_r01 1064 +#define _LOAD_FAST_BORROW_3_r12 1065 +#define _LOAD_FAST_BORROW_3_r23 1066 +#define _LOAD_FAST_BORROW_4_r01 1067 +#define _LOAD_FAST_BORROW_4_r12 1068 +#define _LOAD_FAST_BORROW_4_r23 1069 +#define _LOAD_FAST_BORROW_5_r01 1070 +#define _LOAD_FAST_BORROW_5_r12 1071 +#define _LOAD_FAST_BORROW_5_r23 1072 +#define _LOAD_FAST_BORROW_6_r01 1073 +#define _LOAD_FAST_BORROW_6_r12 1074 +#define _LOAD_FAST_BORROW_6_r23 1075 +#define _LOAD_FAST_BORROW_7_r01 1076 +#define _LOAD_FAST_BORROW_7_r12 1077 +#define _LOAD_FAST_BORROW_7_r23 1078 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1079 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1080 +#define _LOAD_FAST_CHECK_r01 1081 +#define _LOAD_FAST_CHECK_r12 1082 +#define _LOAD_FAST_CHECK_r23 1083 +#define _LOAD_FAST_LOAD_FAST_r02 1084 +#define _LOAD_FAST_LOAD_FAST_r13 1085 +#define _LOAD_FROM_DICT_OR_DEREF_r11 1086 +#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1087 +#define _LOAD_GLOBAL_r00 1088 +#define _LOAD_GLOBAL_BUILTINS_r01 1089 +#define _LOAD_GLOBAL_MODULE_r01 1090 +#define _LOAD_LOCALS_r01 1091 +#define _LOAD_LOCALS_r12 1092 +#define _LOAD_LOCALS_r23 1093 +#define _LOAD_NAME_r01 1094 +#define _LOAD_SMALL_INT_r01 1095 +#define _LOAD_SMALL_INT_r12 1096 +#define _LOAD_SMALL_INT_r23 1097 +#define _LOAD_SMALL_INT_0_r01 1098 +#define _LOAD_SMALL_INT_0_r12 1099 +#define _LOAD_SMALL_INT_0_r23 1100 +#define _LOAD_SMALL_INT_1_r01 1101 +#define _LOAD_SMALL_INT_1_r12 1102 +#define _LOAD_SMALL_INT_1_r23 1103 +#define _LOAD_SMALL_INT_2_r01 1104 +#define _LOAD_SMALL_INT_2_r12 1105 +#define _LOAD_SMALL_INT_2_r23 1106 +#define _LOAD_SMALL_INT_3_r01 1107 +#define _LOAD_SMALL_INT_3_r12 1108 +#define _LOAD_SMALL_INT_3_r23 1109 +#define _LOAD_SPECIAL_r00 1110 +#define _LOAD_SUPER_ATTR_ATTR_r31 1111 +#define _LOAD_SUPER_ATTR_METHOD_r32 1112 +#define _MAKE_CALLARGS_A_TUPLE_r33 1113 +#define _MAKE_CELL_r00 1114 +#define _MAKE_FUNCTION_r11 1115 +#define _MAKE_WARM_r00 1116 +#define _MAKE_WARM_r11 1117 +#define _MAKE_WARM_r22 1118 +#define _MAKE_WARM_r33 1119 +#define _MAP_ADD_r20 1120 +#define _MATCH_CLASS_r31 1121 +#define _MATCH_KEYS_r23 1122 +#define _MATCH_MAPPING_r02 1123 +#define _MATCH_MAPPING_r12 1124 +#define _MATCH_MAPPING_r23 1125 +#define _MATCH_SEQUENCE_r02 1126 +#define _MATCH_SEQUENCE_r12 1127 +#define _MATCH_SEQUENCE_r23 1128 +#define _MAYBE_EXPAND_METHOD_r00 1129 +#define _MAYBE_EXPAND_METHOD_KW_r11 1130 +#define _MONITOR_CALL_r00 1131 +#define _MONITOR_CALL_KW_r11 1132 +#define _MONITOR_JUMP_BACKWARD_r00 1133 +#define _MONITOR_JUMP_BACKWARD_r11 1134 +#define _MONITOR_JUMP_BACKWARD_r22 1135 +#define _MONITOR_JUMP_BACKWARD_r33 1136 +#define _MONITOR_RESUME_r00 1137 +#define _NOP_r00 1138 +#define _NOP_r11 1139 +#define _NOP_r22 1140 +#define _NOP_r33 1141 +#define _POP_CALL_r20 1142 +#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1143 +#define _POP_CALL_ONE_r30 1144 +#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1145 +#define _POP_CALL_TWO_r30 1146 +#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1147 +#define _POP_EXCEPT_r10 1148 +#define _POP_ITER_r20 1149 +#define _POP_JUMP_IF_FALSE_r00 1150 +#define _POP_JUMP_IF_FALSE_r10 1151 +#define _POP_JUMP_IF_FALSE_r21 1152 +#define _POP_JUMP_IF_FALSE_r32 1153 +#define _POP_JUMP_IF_TRUE_r00 1154 +#define _POP_JUMP_IF_TRUE_r10 1155 +#define _POP_JUMP_IF_TRUE_r21 1156 +#define _POP_JUMP_IF_TRUE_r32 1157 +#define _POP_TOP_r10 1158 +#define _POP_TOP_FLOAT_r00 1159 +#define _POP_TOP_FLOAT_r10 1160 +#define _POP_TOP_FLOAT_r21 1161 +#define _POP_TOP_FLOAT_r32 1162 +#define _POP_TOP_INT_r00 1163 +#define _POP_TOP_INT_r10 1164 +#define _POP_TOP_INT_r21 1165 +#define _POP_TOP_INT_r32 1166 +#define _POP_TOP_LOAD_CONST_INLINE_r11 1167 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1168 +#define _POP_TOP_NOP_r00 1169 +#define _POP_TOP_NOP_r10 1170 +#define _POP_TOP_NOP_r21 1171 +#define _POP_TOP_NOP_r32 1172 +#define _POP_TOP_UNICODE_r00 1173 +#define _POP_TOP_UNICODE_r10 1174 +#define _POP_TOP_UNICODE_r21 1175 +#define _POP_TOP_UNICODE_r32 1176 +#define _POP_TWO_r20 1177 +#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1178 +#define _PUSH_EXC_INFO_r02 1179 +#define _PUSH_EXC_INFO_r12 1180 +#define _PUSH_EXC_INFO_r23 1181 +#define _PUSH_FRAME_r10 1182 +#define _PUSH_NULL_r01 1183 +#define _PUSH_NULL_r12 1184 +#define _PUSH_NULL_r23 1185 +#define _PUSH_NULL_CONDITIONAL_r00 1186 +#define _PY_FRAME_GENERAL_r01 1187 +#define _PY_FRAME_KW_r11 1188 +#define _QUICKEN_RESUME_r00 1189 +#define _QUICKEN_RESUME_r11 1190 +#define _QUICKEN_RESUME_r22 1191 +#define _QUICKEN_RESUME_r33 1192 +#define _REPLACE_WITH_TRUE_r11 1193 +#define _RESUME_CHECK_r00 1194 +#define _RESUME_CHECK_r11 1195 +#define _RESUME_CHECK_r22 1196 +#define _RESUME_CHECK_r33 1197 +#define _RETURN_GENERATOR_r01 1198 +#define _RETURN_VALUE_r11 1199 +#define _SAVE_RETURN_OFFSET_r00 1200 +#define _SAVE_RETURN_OFFSET_r11 1201 +#define _SAVE_RETURN_OFFSET_r22 1202 +#define _SAVE_RETURN_OFFSET_r33 1203 +#define _SEND_r22 1204 +#define _SEND_GEN_FRAME_r22 1205 +#define _SETUP_ANNOTATIONS_r00 1206 +#define _SET_ADD_r10 1207 +#define _SET_FUNCTION_ATTRIBUTE_r01 1208 +#define _SET_FUNCTION_ATTRIBUTE_r11 1209 +#define _SET_FUNCTION_ATTRIBUTE_r21 1210 +#define _SET_FUNCTION_ATTRIBUTE_r32 1211 +#define _SET_IP_r00 1212 +#define _SET_IP_r11 1213 +#define _SET_IP_r22 1214 +#define _SET_IP_r33 1215 +#define _SET_UPDATE_r10 1216 +#define _SPILL_OR_RELOAD_r01 1217 +#define _SPILL_OR_RELOAD_r02 1218 +#define _SPILL_OR_RELOAD_r03 1219 +#define _SPILL_OR_RELOAD_r10 1220 +#define _SPILL_OR_RELOAD_r12 1221 +#define _SPILL_OR_RELOAD_r13 1222 +#define _SPILL_OR_RELOAD_r20 1223 +#define _SPILL_OR_RELOAD_r21 1224 +#define _SPILL_OR_RELOAD_r23 1225 +#define _SPILL_OR_RELOAD_r30 1226 +#define _SPILL_OR_RELOAD_r31 1227 +#define _SPILL_OR_RELOAD_r32 1228 +#define _START_EXECUTOR_r00 1229 +#define _STORE_ATTR_r20 1230 +#define _STORE_ATTR_INSTANCE_VALUE_r21 1231 +#define _STORE_ATTR_SLOT_r21 1232 +#define _STORE_ATTR_WITH_HINT_r21 1233 +#define _STORE_DEREF_r10 1234 +#define _STORE_FAST_r10 1235 +#define _STORE_FAST_0_r10 1236 +#define _STORE_FAST_1_r10 1237 +#define _STORE_FAST_2_r10 1238 +#define _STORE_FAST_3_r10 1239 +#define _STORE_FAST_4_r10 1240 +#define _STORE_FAST_5_r10 1241 +#define _STORE_FAST_6_r10 1242 +#define _STORE_FAST_7_r10 1243 +#define _STORE_FAST_LOAD_FAST_r11 1244 +#define _STORE_FAST_STORE_FAST_r20 1245 +#define _STORE_GLOBAL_r10 1246 +#define _STORE_NAME_r10 1247 +#define _STORE_SLICE_r30 1248 +#define _STORE_SUBSCR_r30 1249 +#define _STORE_SUBSCR_DICT_r31 1250 +#define _STORE_SUBSCR_LIST_INT_r32 1251 +#define _SWAP_r11 1252 +#define _SWAP_2_r02 1253 +#define _SWAP_2_r12 1254 +#define _SWAP_2_r22 1255 +#define _SWAP_2_r33 1256 +#define _SWAP_3_r03 1257 +#define _SWAP_3_r13 1258 +#define _SWAP_3_r23 1259 +#define _SWAP_3_r33 1260 +#define _TIER2_RESUME_CHECK_r00 1261 +#define _TIER2_RESUME_CHECK_r11 1262 +#define _TIER2_RESUME_CHECK_r22 1263 +#define _TIER2_RESUME_CHECK_r33 1264 +#define _TO_BOOL_r11 1265 +#define _TO_BOOL_BOOL_r01 1266 +#define _TO_BOOL_BOOL_r11 1267 +#define _TO_BOOL_BOOL_r22 1268 +#define _TO_BOOL_BOOL_r33 1269 +#define _TO_BOOL_INT_r11 1270 +#define _TO_BOOL_LIST_r11 1271 +#define _TO_BOOL_NONE_r01 1272 +#define _TO_BOOL_NONE_r11 1273 +#define _TO_BOOL_NONE_r22 1274 +#define _TO_BOOL_NONE_r33 1275 +#define _TO_BOOL_STR_r11 1276 +#define _TRACE_RECORD_r00 1277 +#define _UNARY_INVERT_r11 1278 +#define _UNARY_NEGATIVE_r11 1279 +#define _UNARY_NOT_r01 1280 +#define _UNARY_NOT_r11 1281 +#define _UNARY_NOT_r22 1282 +#define _UNARY_NOT_r33 1283 +#define _UNPACK_EX_r10 1284 +#define _UNPACK_SEQUENCE_r10 1285 +#define _UNPACK_SEQUENCE_LIST_r10 1286 +#define _UNPACK_SEQUENCE_TUPLE_r10 1287 +#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1288 +#define _WITH_EXCEPT_START_r33 1289 +#define _YIELD_VALUE_r11 1290 +#define MAX_UOP_REGS_ID 1290 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index c6d3670b193..0cc38919565 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -111,7 +111,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = { [_BINARY_OP_MULTIPLY_FLOAT] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_PURE_FLAG, [_BINARY_OP_ADD_FLOAT] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_PURE_FLAG, [_BINARY_OP_SUBTRACT_FLOAT] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_PURE_FLAG, - [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_PURE_FLAG, + [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_PURE_FLAG, [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GUARD_BINARY_OP_EXTEND] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_BINARY_OP_EXTEND] = HAS_ESCAPES_FLAG, @@ -1050,12 +1050,12 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { }, }, [_BINARY_OP_ADD_UNICODE] = { - .best = { 0, 1, 2, 3 }, + .best = { 0, 1, 2, 2 }, .entries = { - { 1, 0, _BINARY_OP_ADD_UNICODE_r01 }, - { 1, 1, _BINARY_OP_ADD_UNICODE_r11 }, - { 1, 2, _BINARY_OP_ADD_UNICODE_r21 }, - { 2, 3, _BINARY_OP_ADD_UNICODE_r32 }, + { 3, 0, _BINARY_OP_ADD_UNICODE_r03 }, + { 3, 1, _BINARY_OP_ADD_UNICODE_r13 }, + { 3, 2, _BINARY_OP_ADD_UNICODE_r23 }, + { -1, -1, -1 }, }, }, [_BINARY_OP_INPLACE_ADD_UNICODE] = { @@ -3414,10 +3414,9 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_BINARY_OP_SUBTRACT_FLOAT_r03] = _BINARY_OP_SUBTRACT_FLOAT, [_BINARY_OP_SUBTRACT_FLOAT_r13] = _BINARY_OP_SUBTRACT_FLOAT, [_BINARY_OP_SUBTRACT_FLOAT_r23] = _BINARY_OP_SUBTRACT_FLOAT, - [_BINARY_OP_ADD_UNICODE_r01] = _BINARY_OP_ADD_UNICODE, - [_BINARY_OP_ADD_UNICODE_r11] = _BINARY_OP_ADD_UNICODE, - [_BINARY_OP_ADD_UNICODE_r21] = _BINARY_OP_ADD_UNICODE, - [_BINARY_OP_ADD_UNICODE_r32] = _BINARY_OP_ADD_UNICODE, + [_BINARY_OP_ADD_UNICODE_r03] = _BINARY_OP_ADD_UNICODE, + [_BINARY_OP_ADD_UNICODE_r13] = _BINARY_OP_ADD_UNICODE, + [_BINARY_OP_ADD_UNICODE_r23] = _BINARY_OP_ADD_UNICODE, [_BINARY_OP_INPLACE_ADD_UNICODE_r20] = _BINARY_OP_INPLACE_ADD_UNICODE, [_GUARD_BINARY_OP_EXTEND_r22] = _GUARD_BINARY_OP_EXTEND, [_BINARY_OP_EXTEND_r21] = _BINARY_OP_EXTEND, @@ -3906,10 +3905,9 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_BINARY_OP_ADD_INT_r13] = "_BINARY_OP_ADD_INT_r13", [_BINARY_OP_ADD_INT_r23] = "_BINARY_OP_ADD_INT_r23", [_BINARY_OP_ADD_UNICODE] = "_BINARY_OP_ADD_UNICODE", - [_BINARY_OP_ADD_UNICODE_r01] = "_BINARY_OP_ADD_UNICODE_r01", - [_BINARY_OP_ADD_UNICODE_r11] = "_BINARY_OP_ADD_UNICODE_r11", - [_BINARY_OP_ADD_UNICODE_r21] = "_BINARY_OP_ADD_UNICODE_r21", - [_BINARY_OP_ADD_UNICODE_r32] = "_BINARY_OP_ADD_UNICODE_r32", + [_BINARY_OP_ADD_UNICODE_r03] = "_BINARY_OP_ADD_UNICODE_r03", + [_BINARY_OP_ADD_UNICODE_r13] = "_BINARY_OP_ADD_UNICODE_r13", + [_BINARY_OP_ADD_UNICODE_r23] = "_BINARY_OP_ADD_UNICODE_r23", [_BINARY_OP_EXTEND] = "_BINARY_OP_EXTEND", [_BINARY_OP_EXTEND_r21] = "_BINARY_OP_EXTEND_r21", [_BINARY_OP_INPLACE_ADD_UNICODE] = "_BINARY_OP_INPLACE_ADD_UNICODE", diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index bed8d1d52e9..6f1b115b312 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2552,6 +2552,22 @@ class TestUopsOptimization(unittest.TestCase): self.assertIn("_POP_TOP_NOP", uops) self.assertNotIn("_POP_TOP", uops) + def test_unicode_add_op_refcount_elimination(self): + def testfunc(n): + c = "a" + res = "" + for _ in range(n): + res = c + c + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, "aa") + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_ADD_UNICODE", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertNotIn("_POP_TOP", uops) + def test_remove_guard_for_slice_list(self): def f(n): for i in range(n): diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 25e730f7e6e..07944f624ed 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -742,7 +742,7 @@ dummy_func( macro(BINARY_OP_SUBTRACT_FLOAT) = _GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/5 + _BINARY_OP_SUBTRACT_FLOAT + _POP_TOP_FLOAT + _POP_TOP_FLOAT; - pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) { + pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(PyUnicode_CheckExact(left_o)); @@ -750,15 +750,17 @@ dummy_func( STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - INPUTS_DEAD(); - ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); + if (PyStackRef_IsNull(res)) { + ERROR_NO_POP(); + } + l = left; + r = right; + INPUTS_DEAD(); } macro(BINARY_OP_ADD_UNICODE) = - _GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_ADD_UNICODE; + _GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_ADD_UNICODE + _POP_TOP_UNICODE + _POP_TOP_UNICODE; // This is a subtle one. It's a super-instruction for // BINARY_OP_ADD_UNICODE followed by STORE_FAST diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 55c4495f2dc..9f3a207929f 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4184,12 +4184,14 @@ break; } - case _BINARY_OP_ADD_UNICODE_r01: { + case _BINARY_OP_ADD_UNICODE_r03: { CHECK_CURRENT_CACHED_VALUES(0); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef right; _PyStackRef left; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -4198,29 +4200,31 @@ assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) { - stack_pointer += -2; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + res = PyStackRef_FromPyObjectSteal(res_o); + if (PyStackRef_IsNull(res)) { SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; _tos_cache0 = res; - SET_CURRENT_CACHED_VALUES(1); + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -2; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } - case _BINARY_OP_ADD_UNICODE_r11: { + case _BINARY_OP_ADD_UNICODE_r13: { CHECK_CURRENT_CACHED_VALUES(1); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef right; _PyStackRef left; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; _PyStackRef _stack_item_0 = _tos_cache0; right = _stack_item_0; left = stack_pointer[-1]; @@ -4230,29 +4234,34 @@ assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) { - stack_pointer += -1; + res = PyStackRef_FromPyObjectSteal(res_o); + if (PyStackRef_IsNull(res)) { + stack_pointer[0] = right; + stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; _tos_cache0 = res; - SET_CURRENT_CACHED_VALUES(1); + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } - case _BINARY_OP_ADD_UNICODE_r21: { + case _BINARY_OP_ADD_UNICODE_r23: { CHECK_CURRENT_CACHED_VALUES(2); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef right; _PyStackRef left; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; right = _stack_item_1; @@ -4263,49 +4272,21 @@ assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) { - SET_CURRENT_CACHED_VALUES(0); - JUMP_TO_ERROR(); - } res = PyStackRef_FromPyObjectSteal(res_o); - _tos_cache0 = res; - SET_CURRENT_CACHED_VALUES(1); - assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); - break; - } - - case _BINARY_OP_ADD_UNICODE_r32: { - CHECK_CURRENT_CACHED_VALUES(3); - assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - _PyStackRef _stack_item_0 = _tos_cache0; - _PyStackRef _stack_item_1 = _tos_cache1; - _PyStackRef _stack_item_2 = _tos_cache2; - right = _stack_item_2; - left = _stack_item_1; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyUnicode_CheckExact(left_o)); - assert(PyUnicode_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) { - stack_pointer[0] = _stack_item_0; - stack_pointer += 1; + if (PyStackRef_IsNull(res)) { + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - res = PyStackRef_FromPyObjectSteal(res_o); - _tos_cache1 = res; - _tos_cache0 = _stack_item_0; - SET_CURRENT_CACHED_VALUES(2); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3c18960a39d..8d3119e9169 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -245,6 +245,8 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; // _GUARD_TOS_UNICODE { value = stack_pointer[-1]; @@ -276,12 +278,24 @@ assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) { - JUMP_TO_LABEL(pop_2_error); - } res = PyStackRef_FromPyObjectSteal(res_o); + if (PyStackRef_IsNull(res)) { + JUMP_TO_LABEL(error); + } + l = left; + r = right; + } + // _POP_TOP_UNICODE + { + value = r; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + // _POP_TOP_UNICODE + { + value = l; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); } stack_pointer[-2] = res; stack_pointer += -1; diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 31ec88e1c84..e3921054f7c 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -298,12 +298,14 @@ dummy_func(void) { r = right; } - op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) { + op(_BINARY_OP_ADD_UNICODE, (left, right -- res, l, r)) { REPLACE_OPCODE_IF_EVALUATES_PURE(left, right); res = sym_new_type(ctx, &PyUnicode_Type); + l = left; + r = right; } - op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- )) { + op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) { JitOptRef res; if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) { assert(PyUnicode_CheckExact(sym_get_const(ctx, left))); @@ -563,6 +565,12 @@ dummy_func(void) { } } + op(_POP_TOP_UNICODE, (value --)) { + if (PyJitRef_IsBorrowed(value)) { + REPLACE_OP(this_instr, _POP_TOP_NOP, 0, 0); + } + } + op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { assert(oparg > 0); top = bottom; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 6fde87a17fb..32d60c76a4f 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -165,6 +165,11 @@ } case _POP_TOP_UNICODE: { + JitOptRef value; + value = stack_pointer[-1]; + if (PyJitRef_IsBorrowed(value)) { + REPLACE_OP(this_instr, _POP_TOP_NOP, 0, 0); + } CHECK_STACK_BOUNDS(-1); stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); @@ -637,6 +642,8 @@ JitOptRef right; JitOptRef left; JitOptRef res; + JitOptRef l; + JitOptRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; if ( @@ -648,6 +655,8 @@ _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); _PyStackRef res_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; /* Start of uop copied from bytecodes for constant evaluation */ PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); @@ -655,31 +664,32 @@ assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) { - goto error; - } res_stackref = PyStackRef_FromPyObjectSteal(res_o); + if (PyStackRef_IsNull(res)) { + JUMP_TO_LABEL(error); + } + l_stackref = left; + r_stackref = right; /* End of uop copied from bytecodes for constant evaluation */ res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); - if (sym_is_const(ctx, res)) { - PyObject *result = sym_get_const(ctx, res); - if (_Py_IsImmortal(result)) { - // Replace with _POP_TWO_LOAD_CONST_INLINE_BORROW since we have two inputs and an immortal result - REPLACE_OP(this_instr, _POP_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); - } - } - CHECK_STACK_BOUNDS(-1); + l = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(l_stackref)); + r = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(r_stackref)); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } res = sym_new_type(ctx, &PyUnicode_Type); - CHECK_STACK_BOUNDS(-1); + l = left; + r = right; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } From 1c544acaa5a83d72839b546af99f8a051437f51f Mon Sep 17 00:00:00 2001 From: MonadChains Date: Thu, 18 Dec 2025 13:50:05 +0100 Subject: [PATCH 33/80] gh-124098: Fix incorrect inclusion of handler methods without protocol prefix in OpenerDirector (GH-136873) --- Lib/test/test_urllib2.py | 17 +++++++++++++++++ Lib/urllib/request.py | 2 ++ ...25-07-20-15-39-54.gh-issue-124098.znFPIp.rst | 4 ++++ 3 files changed, 23 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-07-20-15-39-54.gh-issue-124098.znFPIp.rst diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 7d7f2fa00d3..3a77b9e5ab7 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -577,6 +577,23 @@ class OpenerDirectorTests(unittest.TestCase): self.assertRaises(TypeError, OpenerDirector().add_handler, NonHandler()) + def test_no_protocol_methods(self): + # test the case that methods starts with handler type without the protocol + # like open*() or _open*(). + # These methods should be ignored + + o = OpenerDirector() + meth_spec = [ + ["open"], + ["_open"], + ["error"] + ] + + add_ordered_mock_handlers(o, meth_spec) + + self.assertEqual(len(o.handle_open), 0) + self.assertEqual(len(o.handle_error), 0) + def test_badly_named_methods(self): # test work-around for three methods that accidentally follow the # naming conventions for handler methods diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index 566b8087aec..f32de189b13 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -415,6 +415,8 @@ class OpenerDirector: continue i = meth.find("_") + if i < 1: + continue protocol = meth[:i] condition = meth[i+1:] diff --git a/Misc/NEWS.d/next/Library/2025-07-20-15-39-54.gh-issue-124098.znFPIp.rst b/Misc/NEWS.d/next/Library/2025-07-20-15-39-54.gh-issue-124098.znFPIp.rst new file mode 100644 index 00000000000..236b37d268e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-20-15-39-54.gh-issue-124098.znFPIp.rst @@ -0,0 +1,4 @@ +Fix issue where methods in handlers that lacked the protocol name but +matched a valid base handler method (e.g., ``_open()`` or ``error()``) +were incorrectly added to :class:`urllib.request.OpenerDirector`'s +handlers. Contributed by Andrea Mattei. From ddfc155d3ade5f4867c6789136df73f61d4efb03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Thu, 18 Dec 2025 14:00:12 +0100 Subject: [PATCH 34/80] gh-142784: make the asyncio REPL call `loop.close()` at exit (#142785) --- Lib/asyncio/__main__.py | 1 + .../Library/2025-12-16-04-39-27.gh-issue-142784.HBGJag.rst | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-12-16-04-39-27.gh-issue-142784.HBGJag.rst diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index d078ebfa4ce..89d456b6858 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -240,4 +240,5 @@ if __name__ == '__main__': break console.write('exiting asyncio REPL...\n') + loop.close() sys.exit(return_code) diff --git a/Misc/NEWS.d/next/Library/2025-12-16-04-39-27.gh-issue-142784.HBGJag.rst b/Misc/NEWS.d/next/Library/2025-12-16-04-39-27.gh-issue-142784.HBGJag.rst new file mode 100644 index 00000000000..92a723cbc29 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-16-04-39-27.gh-issue-142784.HBGJag.rst @@ -0,0 +1,3 @@ +The :mod:`asyncio` REPL now properly closes the loop upon the end of interactive session. +Previously, it could cause surprising warnings. +Contributed by Bartosz Sławecki. From 0f01530bd5a12639287b329050a143e442b23061 Mon Sep 17 00:00:00 2001 From: Max R Date: Thu, 18 Dec 2025 08:43:19 -0500 Subject: [PATCH 35/80] Fix typo in `format_string` docstring (GH-136742) --- Lib/locale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/locale.py b/Lib/locale.py index 37cafb4a601..0f1b429ea41 100644 --- a/Lib/locale.py +++ b/Lib/locale.py @@ -214,7 +214,7 @@ def format_string(f, val, grouping=False, monetary=False): Grouping is applied if the third parameter is true. Conversion uses monetary thousands separator and grouping strings if - forth parameter monetary is true.""" + fourth parameter monetary is true.""" global _percent_re if _percent_re is None: import re From d2abd5733b2dc478abb848230d7e8374e74a6927 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 18 Dec 2025 16:22:23 +0200 Subject: [PATCH 36/80] gh-76007: Deprecate `VERSION` in `xml.etree.ElementTree` & `version` in `xml.sax.expatreader` & `xml.sax.handler` (#142898) --- Doc/deprecations/pending-removal-in-3.20.rst | 9 ++++++--- Doc/whatsnew/3.15.rst | 9 ++++++--- Lib/test/test_sax.py | 12 ++++++++++++ Lib/test/test_xml_etree.py | 13 +++++++++++++ Lib/xml/etree/ElementTree.py | 14 +++++++++++--- Lib/xml/sax/expatreader.py | 12 ++++++++++-- Lib/xml/sax/handler.py | 11 +++++++++-- .../2025-12-16-14-21-20.gh-issue-76007.O4AmYl.rst | 3 +++ 8 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-16-14-21-20.gh-issue-76007.O4AmYl.rst diff --git a/Doc/deprecations/pending-removal-in-3.20.rst b/Doc/deprecations/pending-removal-in-3.20.rst index 185f20fbc6d..4e4b2e1d5f8 100644 --- a/Doc/deprecations/pending-removal-in-3.20.rst +++ b/Doc/deprecations/pending-removal-in-3.20.rst @@ -1,9 +1,9 @@ Pending removal in Python 3.20 ------------------------------ -* The ``__version__`` attribute has been deprecated in these standard library - modules and will be removed in Python 3.20. - Use :py:data:`sys.version_info` instead. +* The ``__version__``, ``version`` and ``VERSION`` attributes have been + deprecated in these standard library modules and will be removed in + Python 3.20. Use :py:data:`sys.version_info` instead. - :mod:`argparse` - :mod:`csv` @@ -24,6 +24,9 @@ Pending removal in Python 3.20 - :mod:`tkinter.font` - :mod:`tkinter.ttk` - :mod:`wsgiref.simple_server` + - :mod:`xml.etree.ElementTree` + - :mod:`!xml.sax.expatreader` + - :mod:`xml.sax.handler` - :mod:`zlib` (Contributed by Hugo van Kemenade and Stan Ulbrych in :gh:`76007`.) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 7e032fe5df2..753b1990eb3 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1104,9 +1104,9 @@ New deprecations * ``__version__`` - * The ``__version__`` attribute has been deprecated in these standard library - modules and will be removed in Python 3.20. - Use :py:data:`sys.version_info` instead. + * The ``__version__``, ``version`` and ``VERSION`` attributes have been + deprecated in these standard library modules and will be removed in + Python 3.20. Use :py:data:`sys.version_info` instead. - :mod:`argparse` - :mod:`csv` @@ -1127,6 +1127,9 @@ New deprecations - :mod:`tkinter.font` - :mod:`tkinter.ttk` - :mod:`wsgiref.simple_server` + - :mod:`xml.etree.ElementTree` + - :mod:`!xml.sax.expatreader` + - :mod:`xml.sax.handler` - :mod:`zlib` (Contributed by Hugo van Kemenade and Stan Ulbrych in :gh:`76007`.) diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py index 5c10bcedc69..29babd7bf69 100644 --- a/Lib/test/test_sax.py +++ b/Lib/test/test_sax.py @@ -1573,5 +1573,17 @@ class TestModuleAll(unittest.TestCase): check__all__(self, sax, extra=extra) +class TestModule(unittest.TestCase): + def test_deprecated__version__and__date__(self): + for module in (sax.expatreader, sax.handler): + with self.subTest(module=module): + with self.assertWarnsRegex( + DeprecationWarning, + "'version' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(module, "version") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 87811199706..0178ed02b35 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -4705,6 +4705,19 @@ class C14NTest(unittest.TestCase): # -------------------------------------------------------------------- + +class TestModule(unittest.TestCase): + def test_deprecated_version(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'VERSION' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(ET, "VERSION") + self.assertEqual(cm.filename, __file__) + + +# -------------------------------------------------------------------- + def setUpModule(module=None): # When invoked without a module, runs the Python ET tests by loading pyET. # Otherwise, uses the given module as the ET. diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index d8c0b1b6216..92f902b9a8b 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -83,15 +83,12 @@ __all__ = [ "SubElement", "tostring", "tostringlist", "TreeBuilder", - "VERSION", "XML", "XMLID", "XMLParser", "XMLPullParser", "register_namespace", "canonicalize", "C14NWriterTarget", ] -VERSION = "1.3.0" - import sys import re import warnings @@ -2104,3 +2101,14 @@ except ImportError: pass else: _set_factories(Comment, ProcessingInstruction) + + +# -------------------------------------------------------------------- + +def __getattr__(name): + if name == "VERSION": + from warnings import _deprecated + + _deprecated("VERSION", remove=(3, 20)) + return "1.3.0" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/xml/sax/expatreader.py b/Lib/xml/sax/expatreader.py index ba3c1e98517..37b1add2848 100644 --- a/Lib/xml/sax/expatreader.py +++ b/Lib/xml/sax/expatreader.py @@ -3,8 +3,6 @@ SAX driver for the pyexpat C module. This driver works with pyexpat.__version__ == '2.22'. """ -version = "0.20" - from xml.sax._exceptions import * from xml.sax.handler import feature_validation, feature_namespaces from xml.sax.handler import feature_namespace_prefixes @@ -446,6 +444,16 @@ def create_parser(*args, **kwargs): # --- +def __getattr__(name): + if name == "version": + from warnings import _deprecated + + _deprecated("version", remove=(3, 20)) + return "0.20" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +# --- + if __name__ == "__main__": import xml.sax.saxutils p = create_parser() diff --git a/Lib/xml/sax/handler.py b/Lib/xml/sax/handler.py index 3183c3fe96d..9c2e3af838a 100644 --- a/Lib/xml/sax/handler.py +++ b/Lib/xml/sax/handler.py @@ -9,8 +9,6 @@ of the interfaces. $Id$ """ -version = '2.0beta' - #============================================================================ # # HANDLER INTERFACES @@ -385,3 +383,12 @@ class LexicalHandler: def endCDATA(self): """Reports the end of a CDATA marked section.""" + + +def __getattr__(name): + if name == "version": + from warnings import _deprecated + + _deprecated("version", remove=(3, 20)) + return "2.0beta" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Misc/NEWS.d/next/Library/2025-12-16-14-21-20.gh-issue-76007.O4AmYl.rst b/Misc/NEWS.d/next/Library/2025-12-16-14-21-20.gh-issue-76007.O4AmYl.rst new file mode 100644 index 00000000000..cfda7327e8f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-16-14-21-20.gh-issue-76007.O4AmYl.rst @@ -0,0 +1,3 @@ +Deprecate ``VERSION`` from :mod:`xml.etree.ElementTree` and ``version`` from +:mod:`!xml.sax.expatreader` and :mod:`xml.sax.handler`. Patch by Hugo van +Kemenade. From 14f0b5191ad1d749d2ba5810f4f4495ee5581ce9 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Thu, 18 Dec 2025 23:33:49 +0900 Subject: [PATCH 37/80] gh-142419: Add mmap.set_name method for user custom annotation (gh-142480) --- Doc/library/mmap.rst | 11 +++++ Doc/whatsnew/3.15.rst | 5 ++ Include/internal/pycore_mmap.h | 18 +++---- Lib/test/test_mmap.py | 41 ++++++++++++++++ ...-12-10-02-31-43.gh-issue-142419.C8_LES.rst | 3 ++ Modules/clinic/mmapmodule.c.h | 38 ++++++++++++++- Modules/mmapmodule.c | 48 ++++++++++++++++++- Objects/obmalloc.c | 2 +- Python/jit.c | 2 +- Python/perf_jit_trampoline.c | 3 +- Python/perf_trampoline.c | 2 +- 11 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-10-02-31-43.gh-issue-142419.C8_LES.rst diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst index f32aa322c40..41b90f2c3b3 100644 --- a/Doc/library/mmap.rst +++ b/Doc/library/mmap.rst @@ -328,6 +328,17 @@ To map anonymous memory, -1 should be passed as the fileno along with the length .. versionadded:: 3.13 + .. method:: set_name(name, /) + + Annotate the memory mapping with the given *name* for easier identification + in ``/proc//maps`` if the kernel supports the feature and :option:`-X dev <-X>` is passed + to Python or if Python is built in :ref:`debug mode `. + The length of *name* must not exceed 67 bytes including the ``'\0'`` terminator. + + .. availability:: Linux >= 5.17 (kernel built with ``CONFIG_ANON_VMA_NAME`` option) + + .. versionadded:: next + .. method:: size() Return the length of the file, which can be larger than the size of the diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 753b1990eb3..3c7e28a00c9 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -592,6 +592,11 @@ mmap not be duplicated. (Contributed by Serhiy Storchaka in :gh:`78502`.) +* Added the :meth:`mmap.mmap.set_name` method + to annotate an anonymous memory mapping + if Linux kernel supports :manpage:`PR_SET_VMA_ANON_NAME ` (Linux 5.17 or newer). + (Contributed by Donghee Na in :gh:`142419`.) + os -- diff --git a/Include/internal/pycore_mmap.h b/Include/internal/pycore_mmap.h index 214fd4362a5..897816db010 100644 --- a/Include/internal/pycore_mmap.h +++ b/Include/internal/pycore_mmap.h @@ -17,25 +17,27 @@ extern "C" { #endif #if defined(HAVE_PR_SET_VMA_ANON_NAME) && defined(__linux__) -static inline void +static inline int _PyAnnotateMemoryMap(void *addr, size_t size, const char *name) { #ifndef Py_DEBUG if (!_Py_GetConfig()->dev_mode) { - return; + return 0; } #endif + // The name length cannot exceed 80 (including the '\0'). assert(strlen(name) < 80); - int old_errno = errno; - prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)addr, size, name); - /* Ignore errno from prctl */ - /* See: https://bugzilla.redhat.com/show_bug.cgi?id=2302746 */ - errno = old_errno; + int res = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)addr, size, name); + if (res < 0) { + return -1; + } + return 0; } #else -static inline void +static inline int _PyAnnotateMemoryMap(void *Py_UNUSED(addr), size_t Py_UNUSED(size), const char *Py_UNUSED(name)) { + return 0; } #endif diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 368af0cf89c..aad916ecfe2 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -6,6 +6,7 @@ from test.support import ( from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink from test.support.script_helper import assert_python_ok +import errno import unittest import os import re @@ -1165,6 +1166,46 @@ class MmapTests(unittest.TestCase): m.flush(PAGESIZE) m.flush(PAGESIZE, PAGESIZE) + @unittest.skipUnless(sys.platform == 'linux', 'Linux only') + @support.requires_linux_version(5, 17, 0) + def test_set_name(self): + # Test setting name on anonymous mmap + m = mmap.mmap(-1, PAGESIZE) + self.addCleanup(m.close) + try: + result = m.set_name('test_mapping') + except OSError as exc: + if exc.errno == errno.EINVAL: + # gh-142419: On Fedora, prctl(PR_SET_VMA_ANON_NAME) fails with + # EINVAL because the kernel option CONFIG_ANON_VMA_NAME is + # disabled. + # See: https://bugzilla.redhat.com/show_bug.cgi?id=2302746 + self.skipTest("prctl() failed with EINVAL") + else: + raise + self.assertIsNone(result) + + # Test name length limit (80 chars including prefix "cpython:mmap:" and '\0') + # Prefix is 13 chars, so max name is 66 chars + long_name = 'x' * 66 + result = m.set_name(long_name) + self.assertIsNone(result) + + # Test name too long + too_long_name = 'x' * 67 + with self.assertRaises(ValueError): + m.set_name(too_long_name) + + # Test that file-backed mmap raises error + with open(TESTFN, 'wb+') as f: + f.write(b'x' * PAGESIZE) + f.flush() + m2 = mmap.mmap(f.fileno(), PAGESIZE) + self.addCleanup(m2.close) + + with self.assertRaises(ValueError): + m2.set_name('should_fail') + class LargeMmapTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2025-12-10-02-31-43.gh-issue-142419.C8_LES.rst b/Misc/NEWS.d/next/Library/2025-12-10-02-31-43.gh-issue-142419.C8_LES.rst new file mode 100644 index 00000000000..63955923cd1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-10-02-31-43.gh-issue-142419.C8_LES.rst @@ -0,0 +1,3 @@ +:meth:`mmap.mmap.set_name` method added to annotate an anonymous memory map +if Linux kernel supports ``PR_SET_VMA_ANON_NAME`` (Linux 5.17 or newer). +Patch by Donghee Na. diff --git a/Modules/clinic/mmapmodule.c.h b/Modules/clinic/mmapmodule.c.h index f7fc172b3af..b63f7df2a7e 100644 --- a/Modules/clinic/mmapmodule.c.h +++ b/Modules/clinic/mmapmodule.c.h @@ -479,6 +479,42 @@ exit: return return_value; } +PyDoc_STRVAR(mmap_mmap_set_name__doc__, +"set_name($self, name, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_SET_NAME_METHODDEF \ + {"set_name", (PyCFunction)mmap_mmap_set_name, METH_O, mmap_mmap_set_name__doc__}, + +static PyObject * +mmap_mmap_set_name_impl(mmap_object *self, const char *name); + +static PyObject * +mmap_mmap_set_name(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + const char *name; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("set_name", "argument", "str", arg); + goto exit; + } + Py_ssize_t name_length; + name = PyUnicode_AsUTF8AndSize(arg, &name_length); + if (name == NULL) { + goto exit; + } + if (strlen(name) != (size_t)name_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + return_value = mmap_mmap_set_name_impl((mmap_object *)self, name); + +exit: + return return_value; +} + PyDoc_STRVAR(mmap_mmap_seekable__doc__, "seekable($self, /)\n" "--\n" @@ -796,4 +832,4 @@ exit: #ifndef MMAP_MMAP_MADVISE_METHODDEF #define MMAP_MMAP_MADVISE_METHODDEF #endif /* !defined(MMAP_MMAP_MADVISE_METHODDEF) */ -/*[clinic end generated code: output=381f6cf4986ac867 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fd9ca0ef425af934 input=a9049054013a1b77]*/ diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 37003020de2..ea20fb29c90 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -1117,6 +1117,47 @@ mmap_mmap_seek_impl(mmap_object *self, Py_ssize_t dist, int how) return NULL; } +/*[clinic input] +mmap.mmap.set_name + + name: str + / + +[clinic start generated code]*/ + +static PyObject * +mmap_mmap_set_name_impl(mmap_object *self, const char *name) +/*[clinic end generated code: output=1edaf4fd51277760 input=6c7dd91cad205f07]*/ +{ +#if defined(MAP_ANONYMOUS) && defined(__linux__) + const char *prefix = "cpython:mmap:"; + if (strlen(name) + strlen(prefix) > 79) { + PyErr_SetString(PyExc_ValueError, "name is too long"); + return NULL; + } + if (self->flags & MAP_ANONYMOUS) { + char buf[80]; + sprintf(buf, "%s%s", prefix, name); + if (_PyAnnotateMemoryMap(self->data, self->size, buf) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + Py_RETURN_NONE; + } + else { + /* cannot name non-anonymous mappings */ + PyErr_SetString(PyExc_ValueError, + "Cannot set annotation on non-anonymous mappings"); + return NULL; + } +#else + /* naming not supported on this platform */ + PyErr_SetString(PyExc_NotImplementedError, + "Annotation of mmap is not supported on this platform"); + return NULL; +#endif +} + /*[clinic input] mmap.mmap.seekable @@ -1397,6 +1438,7 @@ static struct PyMethodDef mmap_object_methods[] = { MMAP_MMAP_RESIZE_METHODDEF MMAP_MMAP_SEEK_METHODDEF MMAP_MMAP_SEEKABLE_METHODDEF + MMAP_MMAP_SET_NAME_METHODDEF MMAP_MMAP_SIZE_METHODDEF MMAP_MMAP_TELL_METHODDEF MMAP_MMAP_WRITE_METHODDEF @@ -1952,7 +1994,11 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) PyErr_SetFromErrno(PyExc_OSError); return NULL; } - _PyAnnotateMemoryMap(m_obj->data, map_size, "cpython:mmap"); +#ifdef MAP_ANONYMOUS + if (m_obj->flags & MAP_ANONYMOUS) { + (void)_PyAnnotateMemoryMap(m_obj->data, map_size, "cpython:mmap"); + } +#endif m_obj->access = (access_mode)access; return (PyObject *)m_obj; } diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index b1f9fa2e692..c4ccc9e283f 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -468,7 +468,7 @@ _PyMem_ArenaAlloc(void *Py_UNUSED(ctx), size_t size) if (ptr == MAP_FAILED) return NULL; assert(ptr != NULL); - _PyAnnotateMemoryMap(ptr, size, "cpython:pymalloc"); + (void)_PyAnnotateMemoryMap(ptr, size, "cpython:pymalloc"); return ptr; #else return malloc(size); diff --git a/Python/jit.c b/Python/jit.c index ccafe0ce497..7660f6f9bea 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -77,7 +77,7 @@ jit_alloc(size_t size) unsigned char *memory = mmap(NULL, size, prot, flags, -1, 0); int failed = memory == MAP_FAILED; if (!failed) { - _PyAnnotateMemoryMap(memory, size, "cpython:jit"); + (void)_PyAnnotateMemoryMap(memory, size, "cpython:jit"); } #endif if (failed) { diff --git a/Python/perf_jit_trampoline.c b/Python/perf_jit_trampoline.c index af7d8f9f1ec..0ffa906d85c 100644 --- a/Python/perf_jit_trampoline.c +++ b/Python/perf_jit_trampoline.c @@ -1086,7 +1086,8 @@ static void* perf_map_jit_init(void) { close(fd); return NULL; // Memory mapping failed } - _PyAnnotateMemoryMap(perf_jit_map_state.mapped_buffer, page_size, "cpython:perf_jit_trampoline"); + (void)_PyAnnotateMemoryMap(perf_jit_map_state.mapped_buffer, page_size, + "cpython:perf_jit_trampoline"); #endif perf_jit_map_state.mapped_size = page_size; diff --git a/Python/perf_trampoline.c b/Python/perf_trampoline.c index 669a47ae173..335d8ac7dad 100644 --- a/Python/perf_trampoline.c +++ b/Python/perf_trampoline.c @@ -291,7 +291,7 @@ new_code_arena(void) perf_status = PERF_STATUS_FAILED; return -1; } - _PyAnnotateMemoryMap(memory, mem_size, "cpython:perf_trampoline"); + (void)_PyAnnotateMemoryMap(memory, mem_size, "cpython:perf_trampoline"); void *start = &_Py_trampoline_func_start; void *end = &_Py_trampoline_func_end; size_t code_size = end - start; From e4058d7cb115286c3c53e0a0be2a7b60af46e073 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 18 Dec 2025 16:43:44 +0000 Subject: [PATCH 38/80] GH-142513: Reimplement executor management (GH-142931) * Invalidating an executor does not cause arbitrary code to run * Executors are only freed at safe points --- Include/internal/pycore_interp_structs.h | 1 - Include/internal/pycore_optimizer.h | 7 - Python/ceval_gil.c | 6 + Python/optimizer.c | 181 +++++++++++------------ Python/pystate.c | 1 - 5 files changed, 90 insertions(+), 106 deletions(-) diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 6b3d5711b92..818c4f15959 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -947,7 +947,6 @@ struct _is { struct _PyExecutorObject *executor_deletion_list_head; struct _PyExecutorObject *cold_executor; struct _PyExecutorObject *cold_dynamic_executor; - int executor_deletion_list_remaining_capacity; size_t executor_creation_counter; _rare_events rare_events; PyDict_WatchCallback builtins_dict_watcher; diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index 295d4909e14..3ee62f17283 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -25,7 +25,6 @@ typedef struct { uint8_t opcode; uint8_t oparg; uint8_t valid; - uint8_t linked; uint8_t chain_depth; // Must be big enough for MAX_CHAIN_DEPTH - 1. bool warm; int32_t index; // Index of ENTER_EXECUTOR (if code isn't NULL, below). @@ -55,11 +54,6 @@ typedef struct _PyExecutorObject { _PyExitData exits[1]; } _PyExecutorObject; -/* If pending deletion list gets large enough, then scan, - * and free any executors that aren't executing - * i.e. any that aren't a thread's current_executor. */ -#define EXECUTOR_DELETE_LIST_MAX 100 - // Export for '_opcode' shared extension (JIT compiler). PyAPI_FUNC(_PyExecutorObject*) _Py_GetExecutor(PyCodeObject *code, int offset); @@ -80,7 +74,6 @@ PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyInterpreterState *interp); #else # define _Py_Executors_InvalidateDependency(A, B, C) ((void)0) # define _Py_Executors_InvalidateAll(A, B) ((void)0) -# define _Py_Executors_InvalidateCold(A) ((void)0) #endif diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index f6ada3892f8..88cc66e97f3 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -1397,13 +1397,19 @@ _Py_HandlePending(PyThreadState *tstate) if ((breaker & _PY_GC_SCHEDULED_BIT) != 0) { _Py_unset_eval_breaker_bit(tstate, _PY_GC_SCHEDULED_BIT); _Py_RunGC(tstate); +#ifdef _Py_TIER2 + _Py_ClearExecutorDeletionList(tstate->interp); +#endif } +#ifdef _Py_TIER2 if ((breaker & _PY_EVAL_JIT_INVALIDATE_COLD_BIT) != 0) { _Py_unset_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT); _Py_Executors_InvalidateCold(tstate->interp); tstate->interp->executor_creation_counter = JIT_CLEANUP_THRESHOLD; + _Py_ClearExecutorDeletionList(tstate->interp); } +#endif /* GIL drop request */ if ((breaker & _PY_GIL_DROP_REQUEST_BIT) != 0) { diff --git a/Python/optimizer.c b/Python/optimizer.c index fc984a5374a..1e905850e3d 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -246,8 +246,6 @@ get_oparg(PyObject *self, PyObject *Py_UNUSED(ignored)) ///////////////////// Experimental UOp Optimizer ///////////////////// static int executor_clear(PyObject *executor); -static void unlink_executor(_PyExecutorObject *executor); - void _PyExecutor_Free(_PyExecutorObject *self) @@ -258,63 +256,76 @@ _PyExecutor_Free(_PyExecutorObject *self) PyObject_GC_Del(self); } +static void executor_invalidate(PyObject *op); + +static void +executor_clear_exits(_PyExecutorObject *executor) +{ + _PyExecutorObject *cold = _PyExecutor_GetColdExecutor(); + _PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor(); + for (uint32_t i = 0; i < executor->exit_count; i++) { + _PyExitData *exit = &executor->exits[i]; + exit->temperature = initial_unreachable_backoff_counter(); + _PyExecutorObject *old = executor->exits[i].executor; + exit->executor = exit->is_dynamic ? cold_dynamic : cold; + Py_DECREF(old); + } +} + + void _Py_ClearExecutorDeletionList(PyInterpreterState *interp) { + if (interp->executor_deletion_list_head == NULL) { + return; + } _PyRuntimeState *runtime = &_PyRuntime; HEAD_LOCK(runtime); PyThreadState* ts = PyInterpreterState_ThreadHead(interp); + while (ts) { + _PyExecutorObject *current = (_PyExecutorObject *)ts->current_executor; + Py_XINCREF(current); + ts = ts->next; + } HEAD_UNLOCK(runtime); + _PyExecutorObject *keep_list = NULL; + do { + _PyExecutorObject *exec = interp->executor_deletion_list_head; + interp->executor_deletion_list_head = exec->vm_data.links.next; + if (Py_REFCNT(exec) == 0) { + _PyExecutor_Free(exec); + } else { + exec->vm_data.links.next = keep_list; + keep_list = exec; + } + } while (interp->executor_deletion_list_head != NULL); + interp->executor_deletion_list_head = keep_list; + HEAD_LOCK(runtime); + ts = PyInterpreterState_ThreadHead(interp); while (ts) { _PyExecutorObject *current = (_PyExecutorObject *)ts->current_executor; if (current != NULL) { - /* Anything in this list will be unlinked, so we can reuse the - * linked field as a reachability marker. */ - current->vm_data.linked = 1; + _Py_DECREF_NO_DEALLOC((PyObject *)current); } - HEAD_LOCK(runtime); - ts = PyThreadState_Next(ts); - HEAD_UNLOCK(runtime); + ts = ts->next; } - _PyExecutorObject **prev_to_next_ptr = &interp->executor_deletion_list_head; - _PyExecutorObject *exec = *prev_to_next_ptr; - while (exec != NULL) { - if (exec->vm_data.linked) { - // This executor is currently executing - exec->vm_data.linked = 0; - prev_to_next_ptr = &exec->vm_data.links.next; - } - else { - *prev_to_next_ptr = exec->vm_data.links.next; - _PyExecutor_Free(exec); - } - exec = *prev_to_next_ptr; - } - interp->executor_deletion_list_remaining_capacity = EXECUTOR_DELETE_LIST_MAX; + HEAD_UNLOCK(runtime); } static void add_to_pending_deletion_list(_PyExecutorObject *self) { PyInterpreterState *interp = PyInterpreterState_Get(); + self->vm_data.links.previous = NULL; self->vm_data.links.next = interp->executor_deletion_list_head; interp->executor_deletion_list_head = self; - if (interp->executor_deletion_list_remaining_capacity > 0) { - interp->executor_deletion_list_remaining_capacity--; - } - else { - _Py_ClearExecutorDeletionList(interp); - } } static void uop_dealloc(PyObject *op) { _PyExecutorObject *self = _PyExecutorObject_CAST(op); - _PyObject_GC_UNTRACK(self); + executor_invalidate(op); assert(self->vm_data.code == NULL); - unlink_executor(self); - // Once unlinked it becomes impossible to invalidate an executor, so do it here. - self->vm_data.valid = 0; add_to_pending_deletion_list(self); } @@ -1621,7 +1632,6 @@ link_executor(_PyExecutorObject *executor) head->vm_data.links.previous = executor; interp->executor_list_head = executor; } - executor->vm_data.linked = true; /* executor_list_head must be first in list */ assert(interp->executor_list_head->vm_data.links.previous == NULL); } @@ -1629,11 +1639,7 @@ link_executor(_PyExecutorObject *executor) static void unlink_executor(_PyExecutorObject *executor) { - if (!executor->vm_data.linked) { - return; - } _PyExecutorLinkListNode *links = &executor->vm_data.links; - assert(executor->vm_data.valid); _PyExecutorObject *next = links->next; _PyExecutorObject *prev = links->previous; if (next != NULL) { @@ -1648,7 +1654,6 @@ unlink_executor(_PyExecutorObject *executor) assert(interp->executor_list_head == executor); interp->executor_list_head = next; } - executor->vm_data.linked = false; } /* This must be called by optimizers before using the executor */ @@ -1662,61 +1667,47 @@ _Py_ExecutorInit(_PyExecutorObject *executor, const _PyBloomFilter *dependency_s link_executor(executor); } -_PyExecutorObject * -_PyExecutor_GetColdExecutor(void) +static _PyExecutorObject * +make_cold_executor(uint16_t opcode) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->cold_executor != NULL) { - return interp->cold_executor; - } _PyExecutorObject *cold = allocate_executor(0, 1); if (cold == NULL) { Py_FatalError("Cannot allocate core JIT code"); } - ((_PyUOpInstruction *)cold->trace)->opcode = _COLD_EXIT_r00; -#ifdef _Py_JIT - cold->jit_code = NULL; - cold->jit_size = 0; + ((_PyUOpInstruction *)cold->trace)->opcode = opcode; // This is initialized to true so we can prevent the executor // from being immediately detected as cold and invalidated. cold->vm_data.warm = true; +#ifdef _Py_JIT + cold->jit_code = NULL; + cold->jit_size = 0; if (_PyJIT_Compile(cold, cold->trace, 1)) { Py_DECREF(cold); Py_FatalError("Cannot allocate core JIT code"); } #endif _Py_SetImmortal((PyObject *)cold); - interp->cold_executor = cold; return cold; } +_PyExecutorObject * +_PyExecutor_GetColdExecutor(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (interp->cold_executor == NULL) { + return interp->cold_executor = make_cold_executor(_COLD_EXIT_r00);; + } + return interp->cold_executor; +} + _PyExecutorObject * _PyExecutor_GetColdDynamicExecutor(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->cold_dynamic_executor != NULL) { - assert(interp->cold_dynamic_executor->trace[0].opcode == _COLD_DYNAMIC_EXIT_r00); - return interp->cold_dynamic_executor; + if (interp->cold_dynamic_executor == NULL) { + interp->cold_dynamic_executor = make_cold_executor(_COLD_DYNAMIC_EXIT_r00); } - _PyExecutorObject *cold = allocate_executor(0, 1); - if (cold == NULL) { - Py_FatalError("Cannot allocate core JIT code"); - } - ((_PyUOpInstruction *)cold->trace)->opcode = _COLD_DYNAMIC_EXIT_r00; -#ifdef _Py_JIT - cold->jit_code = NULL; - cold->jit_size = 0; - // This is initialized to true so we can prevent the executor - // from being immediately detected as cold and invalidated. - cold->vm_data.warm = true; - if (_PyJIT_Compile(cold, cold->trace, 1)) { - Py_DECREF(cold); - Py_FatalError("Cannot allocate core JIT code"); - } -#endif - _Py_SetImmortal((PyObject *)cold); - interp->cold_dynamic_executor = cold; - return cold; + return interp->cold_dynamic_executor; } void @@ -1755,32 +1746,28 @@ _Py_ExecutorDetach(_PyExecutorObject *executor) Py_DECREF(executor); } -static int -executor_clear(PyObject *op) +/* Executors can be invalidated at any time, + even with a stop-the-world lock held. + Consequently it must not run arbitrary code, + including Py_DECREF with a non-executor. */ +static void +executor_invalidate(PyObject *op) { _PyExecutorObject *executor = _PyExecutorObject_CAST(op); if (!executor->vm_data.valid) { - return 0; + return; } - assert(executor->vm_data.valid == 1); - unlink_executor(executor); executor->vm_data.valid = 0; - - /* It is possible for an executor to form a reference - * cycle with itself, so decref'ing a side exit could - * free the executor unless we hold a strong reference to it - */ - _PyExecutorObject *cold = _PyExecutor_GetColdExecutor(); - Py_INCREF(executor); - for (uint32_t i = 0; i < executor->exit_count; i++) { - executor->exits[i].temperature = initial_unreachable_backoff_counter(); - _PyExecutorObject *e = executor->exits[i].executor; - executor->exits[i].executor = cold; - Py_DECREF(e); - } + unlink_executor(executor); + executor_clear_exits(executor); _Py_ExecutorDetach(executor); - Py_DECREF(executor); - return 0; + _PyObject_GC_UNTRACK(op); +} + +static int +executor_clear(PyObject *op) +{ + executor_invalidate(op); } void @@ -1805,7 +1792,7 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is if (invalidate == NULL) { goto error; } - /* Clearing an executor can deallocate others, so we need to make a list of + /* Clearing an executor can clear others, so we need to make a list of * executors to invalidate first */ for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) { assert(exec->vm_data.valid); @@ -1819,7 +1806,7 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is } for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) { PyObject *exec = PyList_GET_ITEM(invalidate, i); - executor_clear(exec); + executor_invalidate(exec); if (is_invalidation) { OPT_STAT_INC(executors_invalidated); } @@ -1851,13 +1838,13 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation) { while (interp->executor_list_head) { _PyExecutorObject *executor = interp->executor_list_head; - assert(executor->vm_data.valid == 1 && executor->vm_data.linked == 1); + assert(executor->vm_data.valid); if (executor->vm_data.code) { // Clear the entire code object so its co_executors array be freed: _PyCode_Clear_Executors(executor->vm_data.code); } else { - executor_clear((PyObject *)executor); + executor_invalidate((PyObject *)executor); } if (is_invalidation) { OPT_STAT_INC(executors_invalidated); @@ -1892,7 +1879,7 @@ _Py_Executors_InvalidateCold(PyInterpreterState *interp) } for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) { PyObject *exec = PyList_GET_ITEM(invalidate, i); - executor_clear(exec); + executor_invalidate(exec); } Py_DECREF(invalidate); return; diff --git a/Python/pystate.c b/Python/pystate.c index 4bf89a23426..7ea8ef91f10 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -570,7 +570,6 @@ init_interpreter(PyInterpreterState *interp, interp->compiling = false; interp->executor_list_head = NULL; interp->executor_deletion_list_head = NULL; - interp->executor_deletion_list_remaining_capacity = 0; interp->executor_creation_counter = JIT_CLEANUP_THRESHOLD; if (interp != &runtime->_main_interpreter) { /* Fix the self-referential, statically initialized fields. */ From 4a8ecbad809dedd9268973d533f24117dfc2e5ba Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 18 Dec 2025 19:04:28 +0200 Subject: [PATCH 39/80] gh-142681: Move NormalizationTest-3.2.0.txt to more safe place. (GH-142935) --- Lib/test/{data => }/NormalizationTest-3.2.0.txt | 0 Lib/test/test_unicodedata.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Lib/test/{data => }/NormalizationTest-3.2.0.txt (100%) diff --git a/Lib/test/data/NormalizationTest-3.2.0.txt b/Lib/test/NormalizationTest-3.2.0.txt similarity index 100% rename from Lib/test/data/NormalizationTest-3.2.0.txt rename to Lib/test/NormalizationTest-3.2.0.txt diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py index f5ab25c602a..b1888b6db9b 100644 --- a/Lib/test/test_unicodedata.py +++ b/Lib/test/test_unicodedata.py @@ -759,7 +759,7 @@ class NormalizationTest(unittest.TestCase): @requires_resource('cpu') def test_normalization_3_2_0(self): - testdatafile = findfile('NormalizationTest-3.2.0.txt', 'data') + testdatafile = findfile('NormalizationTest-3.2.0.txt') with open(testdatafile, encoding='utf-8') as testdata: self.run_normalization_tests(testdata, unicodedata.ucd_3_2_0) From e22c49522bd119d2d2690989ea269e2f5a3009db Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 18 Dec 2025 22:48:56 +0530 Subject: [PATCH 40/80] gh-142890: remove unnecessary interp parameter from dict functions and `_PyDict_NotifyEvent` (#142923) --- Include/internal/pycore_dict.h | 3 +- Objects/dictobject.c | 105 +++++++++++++-------------------- Python/bytecodes.c | 2 +- Python/executor_cases.c.h | 2 +- Python/generated_cases.c.h | 2 +- 5 files changed, 46 insertions(+), 68 deletions(-) diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 1193f496da1..a7005a3b8e2 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -272,8 +272,7 @@ _PyDict_SendEvent(int watcher_bits, PyObject *value); static inline void -_PyDict_NotifyEvent(PyInterpreterState *interp, - PyDict_WatchEvent event, +_PyDict_NotifyEvent(PyDict_WatchEvent event, PyDictObject *mp, PyObject *key, PyObject *value) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 49a42a35acb..5a2bb7d3d8c 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1771,7 +1771,7 @@ insertion_resize(PyDictObject *mp, int unicode) } static inline int -insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp, +insert_combined_dict(PyDictObject *mp, Py_hash_t hash, PyObject *key, PyObject *value) { // gh-140551: If dict was cleared in _Py_dict_lookup, @@ -1789,7 +1789,7 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp, } } - _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, value); FT_ATOMIC_STORE_UINT32_RELAXED(mp->ma_keys->dk_version, 0); Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); @@ -1846,19 +1846,19 @@ insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash) } static void -insert_split_value(PyInterpreterState *interp, PyDictObject *mp, PyObject *key, PyObject *value, Py_ssize_t ix) +insert_split_value(PyDictObject *mp, PyObject *key, PyObject *value, Py_ssize_t ix) { assert(PyUnicode_CheckExact(key)); ASSERT_DICT_LOCKED(mp); PyObject *old_value = mp->ma_values->values[ix]; if (old_value == NULL) { - _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, value); STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value)); _PyDictValues_AddToInsertionOrder(mp->ma_values, ix); STORE_USED(mp, mp->ma_used + 1); } else { - _PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, mp, key, value); STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value)); // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. @@ -1874,7 +1874,7 @@ Returns -1 if an error occurred, or 0 on success. Consumes key and value references. */ static int -insertdict(PyInterpreterState *interp, PyDictObject *mp, +insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { PyObject *old_value; @@ -1885,7 +1885,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) { ix = insert_split_key(mp->ma_keys, key, hash); if (ix != DKIX_EMPTY) { - insert_split_value(interp, mp, key, value, ix); + insert_split_value(mp, key, value, ix); Py_DECREF(key); Py_DECREF(value); return 0; @@ -1903,7 +1903,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, // into DICT_KEYS_GENERAL table if key is not Unicode. // We don't convert it before _Py_dict_lookup because non-Unicode key // may change generic table into Unicode table. - if (insert_combined_dict(interp, mp, hash, key, value) < 0) { + if (insert_combined_dict(mp, hash, key, value) < 0) { goto Fail; } STORE_USED(mp, mp->ma_used + 1); @@ -1912,7 +1912,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, } if (old_value != value) { - _PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, mp, key, value); assert(old_value != NULL); if (DK_IS_UNICODE(mp->ma_keys)) { if (_PyDict_HasSplitTable(mp)) { @@ -1942,7 +1942,7 @@ Fail: // Same as insertdict but specialized for ma_keys == Py_EMPTY_KEYS. // Consumes key and value references. static int -insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp, +insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { assert(mp->ma_keys == Py_EMPTY_KEYS); @@ -1955,7 +1955,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp, Py_DECREF(value); return -1; } - _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, value); /* We don't decref Py_EMPTY_KEYS here because it is immortal. */ assert(mp->ma_values == NULL); @@ -2658,13 +2658,11 @@ setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) return -1; } - PyInterpreterState *interp = _PyInterpreterState_GET(); - if (mp->ma_keys == Py_EMPTY_KEYS) { - return insert_to_emptydict(interp, mp, key, hash, value); + return insert_to_emptydict(mp, key, hash, value); } /* insertdict() handles any resizing that might be necessary */ - return insertdict(interp, mp, key, hash, value); + return insertdict(mp, key, hash, value); } int @@ -2710,12 +2708,11 @@ int _PyDict_SetItem_KnownHash_LockHeld(PyDictObject *mp, PyObject *key, PyObject *value, Py_hash_t hash) { - PyInterpreterState *interp = _PyInterpreterState_GET(); if (mp->ma_keys == Py_EMPTY_KEYS) { - return insert_to_emptydict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value)); + return insert_to_emptydict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } /* insertdict() handles any resizing that might be necessary */ - return insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value)); + return insertdict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } int @@ -2836,8 +2833,7 @@ _PyDict_DelItem_KnownHash_LockHeld(PyObject *op, PyObject *key, Py_hash_t hash) return -1; } - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, mp, key, NULL); delitem_common(mp, hash, ix, old_value); return 0; } @@ -2883,8 +2879,7 @@ delitemif_lock_held(PyObject *op, PyObject *key, return -1; if (res > 0) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, mp, key, NULL); delitem_common(mp, hash, ix, old_value); return 1; } else { @@ -2928,8 +2923,7 @@ clear_lock_held(PyObject *op) return; } /* Empty the dict... */ - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyDict_NotifyEvent(interp, PyDict_EVENT_CLEARED, mp, NULL, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_CLEARED, mp, NULL, NULL); // We don't inc ref empty keys because they're immortal ensure_shared_on_resize(mp); STORE_USED(mp, 0); @@ -3095,8 +3089,7 @@ _PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash, } assert(old_value != NULL); - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, mp, key, NULL); delitem_common(mp, hash, ix, Py_NewRef(old_value)); ASSERT_CONSISTENT(mp); @@ -3191,8 +3184,7 @@ _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *default_value) } static PyDictObject * -dict_dict_fromkeys(PyInterpreterState *interp, PyDictObject *mp, - PyObject *iterable, PyObject *value) +dict_dict_fromkeys(PyDictObject *mp, PyObject *iterable, PyObject *value) { PyObject *oldvalue; Py_ssize_t pos = 0; @@ -3208,8 +3200,7 @@ dict_dict_fromkeys(PyInterpreterState *interp, PyDictObject *mp, } while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { - if (insertdict(interp, mp, - Py_NewRef(key), hash, Py_NewRef(value))) { + if (insertdict(mp, Py_NewRef(key), hash, Py_NewRef(value))) { Py_DECREF(mp); return NULL; } @@ -3218,8 +3209,7 @@ dict_dict_fromkeys(PyInterpreterState *interp, PyDictObject *mp, } static PyDictObject * -dict_set_fromkeys(PyInterpreterState *interp, PyDictObject *mp, - PyObject *iterable, PyObject *value) +dict_set_fromkeys(PyDictObject *mp, PyObject *iterable, PyObject *value) { Py_ssize_t pos = 0; PyObject *key; @@ -3234,7 +3224,7 @@ dict_set_fromkeys(PyInterpreterState *interp, PyDictObject *mp, _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(iterable); while (_PySet_NextEntryRef(iterable, &pos, &key, &hash)) { - if (insertdict(interp, mp, key, hash, Py_NewRef(value))) { + if (insertdict(mp, key, hash, Py_NewRef(value))) { Py_DECREF(mp); return NULL; } @@ -3250,7 +3240,6 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) PyObject *key; PyObject *d; int status; - PyInterpreterState *interp = _PyInterpreterState_GET(); d = _PyObject_CallNoArgs(cls); if (d == NULL) @@ -3262,7 +3251,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) PyDictObject *mp = (PyDictObject *)d; Py_BEGIN_CRITICAL_SECTION2(d, iterable); - d = (PyObject *)dict_dict_fromkeys(interp, mp, iterable, value); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); Py_END_CRITICAL_SECTION2(); return d; } @@ -3270,7 +3259,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) PyDictObject *mp = (PyDictObject *)d; Py_BEGIN_CRITICAL_SECTION2(d, iterable); - d = (PyObject *)dict_set_fromkeys(interp, mp, iterable, value); + d = (PyObject *)dict_set_fromkeys(mp, iterable, value); Py_END_CRITICAL_SECTION2(); return d; } @@ -3320,9 +3309,8 @@ static void dict_dealloc(PyObject *self) { PyDictObject *mp = (PyDictObject *)self; - PyInterpreterState *interp = _PyInterpreterState_GET(); _PyObject_ResurrectStart(self); - _PyDict_NotifyEvent(interp, PyDict_EVENT_DEALLOCATED, mp, NULL, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DEALLOCATED, mp, NULL, NULL); if (_PyObject_ResurrectEnd(self)) { return; } @@ -3844,7 +3832,7 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) } static int -dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *other, int override) +dict_dict_merge(PyDictObject *mp, PyDictObject *other, int override) { ASSERT_DICT_LOCKED(mp); ASSERT_DICT_LOCKED(other); @@ -3867,7 +3855,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe (DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE || USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used) ) { - _PyDict_NotifyEvent(interp, PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL); PyDictKeysObject *keys = clone_combined_dict_keys(other); if (keys == NULL) return -1; @@ -3908,14 +3896,12 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe Py_INCREF(key); Py_INCREF(value); if (override == 1) { - err = insertdict(interp, mp, - Py_NewRef(key), hash, Py_NewRef(value)); + err = insertdict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } else { err = _PyDict_Contains_KnownHash((PyObject *)mp, key, hash); if (err == 0) { - err = insertdict(interp, mp, - Py_NewRef(key), hash, Py_NewRef(value)); + err = insertdict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } else if (err > 0) { if (override != 0) { @@ -3942,7 +3928,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe } static int -dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override) +dict_merge(PyObject *a, PyObject *b, int override) { PyDictObject *mp, *other; @@ -3963,7 +3949,7 @@ dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override) other = (PyDictObject*)b; int res; Py_BEGIN_CRITICAL_SECTION2(a, b); - res = dict_dict_merge(interp, (PyDictObject *)a, other, override); + res = dict_dict_merge((PyDictObject *)a, other, override); ASSERT_CONSISTENT(a); Py_END_CRITICAL_SECTION2(); return res; @@ -4044,23 +4030,20 @@ slow_exit: int PyDict_Update(PyObject *a, PyObject *b) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return dict_merge(interp, a, b, 1); + return dict_merge(a, b, 1); } int PyDict_Merge(PyObject *a, PyObject *b, int override) { - PyInterpreterState *interp = _PyInterpreterState_GET(); /* XXX Deprecate override not in (0, 1). */ - return dict_merge(interp, a, b, override != 0); + return dict_merge(a, b, override != 0); } int _PyDict_MergeEx(PyObject *a, PyObject *b, int override) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return dict_merge(interp, a, b, override); + return dict_merge(a, b, override); } /*[clinic input] @@ -4102,7 +4085,6 @@ copy_lock_held(PyObject *o) { PyObject *copy; PyDictObject *mp; - PyInterpreterState *interp = _PyInterpreterState_GET(); ASSERT_DICT_LOCKED(o); @@ -4172,7 +4154,7 @@ copy_lock_held(PyObject *o) copy = PyDict_New(); if (copy == NULL) return NULL; - if (dict_merge(interp, copy, o, 1) == 0) + if (dict_merge(copy, o, 1) == 0) return copy; Py_DECREF(copy); return NULL; @@ -4367,7 +4349,6 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu PyObject *value; Py_hash_t hash; Py_ssize_t ix; - PyInterpreterState *interp = _PyInterpreterState_GET(); ASSERT_DICT_LOCKED(d); @@ -4389,7 +4370,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu } if (mp->ma_keys == Py_EMPTY_KEYS) { - if (insert_to_emptydict(interp, mp, Py_NewRef(key), hash, + if (insert_to_emptydict(mp, Py_NewRef(key), hash, Py_NewRef(default_value)) < 0) { if (result) { *result = NULL; @@ -4408,7 +4389,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu PyObject *value = mp->ma_values->values[ix]; int already_present = value != NULL; if (!already_present) { - insert_split_value(interp, mp, key, default_value, ix); + insert_split_value(mp, key, default_value, ix); value = default_value; } if (result) { @@ -4432,7 +4413,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu value = default_value; // See comment to this function in insertdict. - if (insert_combined_dict(interp, mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) { + if (insert_combined_dict(mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) { Py_DECREF(key); Py_DECREF(value); if (result) { @@ -4554,7 +4535,6 @@ dict_popitem_impl(PyDictObject *self) { Py_ssize_t i, j; PyObject *res; - PyInterpreterState *interp = _PyInterpreterState_GET(); ASSERT_DICT_LOCKED(self); @@ -4596,7 +4576,7 @@ dict_popitem_impl(PyDictObject *self) assert(i >= 0); key = ep0[i].me_key; - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, self, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, self, key, NULL); hash = unicode_get_hash(key); value = ep0[i].me_value; STORE_KEY(&ep0[i], NULL); @@ -4611,7 +4591,7 @@ dict_popitem_impl(PyDictObject *self) assert(i >= 0); key = ep0[i].me_key; - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, self, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, self, key, NULL); hash = ep0[i].me_hash; value = ep0[i].me_value; STORE_KEY(&ep0[i], NULL); @@ -6925,11 +6905,10 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values, } if (dict) { - PyInterpreterState *interp = _PyInterpreterState_GET(); PyDict_WatchEvent event = (old_value == NULL ? PyDict_EVENT_ADDED : value == NULL ? PyDict_EVENT_DELETED : PyDict_EVENT_MODIFIED); - _PyDict_NotifyEvent(interp, event, dict, name, value); + _PyDict_NotifyEvent(event, dict, name, value); } FT_ATOMIC_STORE_PTR_RELEASE(values->values[ix], Py_XNewRef(value)); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 07944f624ed..3e3b3d2f446 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2628,7 +2628,7 @@ dummy_func( UNLOCK_OBJECT(dict); DEOPT_IF(true); } - _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(dict); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 9f3a207929f..b7ee752cd44 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -8825,7 +8825,7 @@ stack_pointer += 2; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(dict); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 8d3119e9169..28dc7b25f0d 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -10822,7 +10822,7 @@ } } _PyFrame_SetStackPointer(frame, stack_pointer); - _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(dict); From f54d44d3331ef0bc19f1437f32ffde2e49c07c22 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 18 Dec 2025 13:11:51 -0500 Subject: [PATCH 41/80] gh-129068: Make range iterators thread-safe (gh-142886) Now that we specialize range iteration in the interpreter for the common case where the iterator has only one reference, there's not a significant performance cost to making the iteration thread-safe. --- ...-12-17-10-49-03.gh-issue-129068.GlYnrO.rst | 2 + Objects/clinic/rangeobject.c.h | 150 ++++++++++++++++++ Objects/rangeobject.c | 131 +++++++++++---- Tools/tsan/suppressions_free_threading.txt | 6 - 4 files changed, 251 insertions(+), 38 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-10-49-03.gh-issue-129068.GlYnrO.rst create mode 100644 Objects/clinic/rangeobject.c.h diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-10-49-03.gh-issue-129068.GlYnrO.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-10-49-03.gh-issue-129068.GlYnrO.rst new file mode 100644 index 00000000000..16b19524cd4 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-10-49-03.gh-issue-129068.GlYnrO.rst @@ -0,0 +1,2 @@ +Make concurrent iteration over the same range iterator thread-safe in the +free threading build. diff --git a/Objects/clinic/rangeobject.c.h b/Objects/clinic/rangeobject.c.h new file mode 100644 index 00000000000..d9142eddde4 --- /dev/null +++ b/Objects/clinic/rangeobject.c.h @@ -0,0 +1,150 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() + +PyDoc_STRVAR(range_iterator___length_hint____doc__, +"__length_hint__($self, /)\n" +"--\n" +"\n" +"Private method returning an estimate of len(list(it))."); + +#define RANGE_ITERATOR___LENGTH_HINT___METHODDEF \ + {"__length_hint__", (PyCFunction)range_iterator___length_hint__, METH_NOARGS, range_iterator___length_hint____doc__}, + +static PyObject * +range_iterator___length_hint___impl(_PyRangeIterObject *r); + +static PyObject * +range_iterator___length_hint__(PyObject *r, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = range_iterator___length_hint___impl((_PyRangeIterObject *)r); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(range_iterator___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n" +"Return state information for pickling."); + +#define RANGE_ITERATOR___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)range_iterator___reduce__, METH_NOARGS, range_iterator___reduce____doc__}, + +static PyObject * +range_iterator___reduce___impl(_PyRangeIterObject *r); + +static PyObject * +range_iterator___reduce__(PyObject *r, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = range_iterator___reduce___impl((_PyRangeIterObject *)r); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(range_iterator___setstate____doc__, +"__setstate__($self, state, /)\n" +"--\n" +"\n" +"Set state information for unpickling."); + +#define RANGE_ITERATOR___SETSTATE___METHODDEF \ + {"__setstate__", (PyCFunction)range_iterator___setstate__, METH_O, range_iterator___setstate____doc__}, + +static PyObject * +range_iterator___setstate___impl(_PyRangeIterObject *r, PyObject *state); + +static PyObject * +range_iterator___setstate__(PyObject *r, PyObject *state) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = range_iterator___setstate___impl((_PyRangeIterObject *)r, state); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(longrange_iterator___length_hint____doc__, +"__length_hint__($self, /)\n" +"--\n" +"\n" +"Private method returning an estimate of len(list(it))."); + +#define LONGRANGE_ITERATOR___LENGTH_HINT___METHODDEF \ + {"__length_hint__", (PyCFunction)longrange_iterator___length_hint__, METH_NOARGS, longrange_iterator___length_hint____doc__}, + +static PyObject * +longrange_iterator___length_hint___impl(longrangeiterobject *r); + +static PyObject * +longrange_iterator___length_hint__(PyObject *r, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = longrange_iterator___length_hint___impl((longrangeiterobject *)r); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(longrange_iterator___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n" +"Return state information for pickling."); + +#define LONGRANGE_ITERATOR___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)longrange_iterator___reduce__, METH_NOARGS, longrange_iterator___reduce____doc__}, + +static PyObject * +longrange_iterator___reduce___impl(longrangeiterobject *r); + +static PyObject * +longrange_iterator___reduce__(PyObject *r, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = longrange_iterator___reduce___impl((longrangeiterobject *)r); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(longrange_iterator___setstate____doc__, +"__setstate__($self, state, /)\n" +"--\n" +"\n" +"Set state information for unpickling."); + +#define LONGRANGE_ITERATOR___SETSTATE___METHODDEF \ + {"__setstate__", (PyCFunction)longrange_iterator___setstate__, METH_O, longrange_iterator___setstate____doc__}, + +static PyObject * +longrange_iterator___setstate___impl(longrangeiterobject *r, PyObject *state); + +static PyObject * +longrange_iterator___setstate__(PyObject *r, PyObject *state) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = longrange_iterator___setstate___impl((longrangeiterobject *)r, state); + Py_END_CRITICAL_SECTION(); + + return return_value; +} +/*[clinic end generated code: output=719c0e4c81fe0f4a input=a9049054013a1b77]*/ diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index e93346fb277..55b7f108730 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -9,6 +9,21 @@ #include "pycore_range.h" #include "pycore_tuple.h" // _PyTuple_ITEMS() +typedef struct { + PyObject_HEAD + PyObject *start; + PyObject *step; + PyObject *len; +} longrangeiterobject; + +/*[clinic input] +class range_iterator "_PyRangeIterObject *" "&PyRangeIter_Type" +class longrange_iterator "longrangeiterobject *" "&PyLongRangeIter_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c7d97a63d1cfa6b3]*/ + +#include "clinic/rangeobject.c.h" + /* Support objects whose length is > PY_SSIZE_T_MAX. @@ -830,30 +845,46 @@ PyTypeObject PyRange_Type = { static PyObject * rangeiter_next(PyObject *op) { + PyObject *ret = NULL; + Py_BEGIN_CRITICAL_SECTION(op); _PyRangeIterObject *r = (_PyRangeIterObject*)op; if (r->len > 0) { long result = r->start; r->start = result + r->step; r->len--; - return PyLong_FromLong(result); + ret = PyLong_FromLong(result); } - return NULL; + Py_END_CRITICAL_SECTION(); + return ret; } +/*[clinic input] +@critical_section +range_iterator.__length_hint__ + self as r: self(type="_PyRangeIterObject *") + +Private method returning an estimate of len(list(it)). +[clinic start generated code]*/ + static PyObject * -rangeiter_len(PyObject *op, PyObject *Py_UNUSED(ignored)) +range_iterator___length_hint___impl(_PyRangeIterObject *r) +/*[clinic end generated code: output=9ba6f22b1fc23dcc input=e3eb311e99d76e43]*/ { - _PyRangeIterObject *r = (_PyRangeIterObject*)op; return PyLong_FromLong(r->len); } -PyDoc_STRVAR(length_hint_doc, - "Private method returning an estimate of len(list(it))."); +/*[clinic input] +@critical_section +range_iterator.__reduce__ + self as r: self(type="_PyRangeIterObject *") + +Return state information for pickling. +[clinic start generated code]*/ static PyObject * -rangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) +range_iterator___reduce___impl(_PyRangeIterObject *r) +/*[clinic end generated code: output=c44d53750c388415 input=75a25b7076dc2c54]*/ { - _PyRangeIterObject *r = (_PyRangeIterObject*)op; PyObject *start=NULL, *stop=NULL, *step=NULL; PyObject *range; @@ -881,10 +912,20 @@ err: return NULL; } +/*[clinic input] +@critical_section +range_iterator.__setstate__ + self as r: self(type="_PyRangeIterObject *") + state: object + / + +Set state information for unpickling. +[clinic start generated code]*/ + static PyObject * -rangeiter_setstate(PyObject *op, PyObject *state) +range_iterator___setstate___impl(_PyRangeIterObject *r, PyObject *state) +/*[clinic end generated code: output=464b3cbafc2e3562 input=c8c84fab2519d200]*/ { - _PyRangeIterObject *r = (_PyRangeIterObject*)op; long index = PyLong_AsLong(state); if (index == -1 && PyErr_Occurred()) return NULL; @@ -904,13 +945,10 @@ rangeiter_dealloc(PyObject *self) _Py_FREELIST_FREE(range_iters, (_PyRangeIterObject *)self, PyObject_Free); } -PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); -PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); - static PyMethodDef rangeiter_methods[] = { - {"__length_hint__", rangeiter_len, METH_NOARGS, length_hint_doc}, - {"__reduce__", rangeiter_reduce, METH_NOARGS, reduce_doc}, - {"__setstate__", rangeiter_setstate, METH_O, setstate_doc}, + RANGE_ITERATOR___LENGTH_HINT___METHODDEF + RANGE_ITERATOR___REDUCE___METHODDEF + RANGE_ITERATOR___SETSTATE___METHODDEF {NULL, NULL} /* sentinel */ }; @@ -995,25 +1033,34 @@ fast_range_iter(long start, long stop, long step, long len) return (PyObject *)it; } -typedef struct { - PyObject_HEAD - PyObject *start; - PyObject *step; - PyObject *len; -} longrangeiterobject; +/*[clinic input] +@critical_section +longrange_iterator.__length_hint__ + self as r: self(type="longrangeiterobject *") + +Private method returning an estimate of len(list(it)). +[clinic start generated code]*/ static PyObject * -longrangeiter_len(PyObject *op, PyObject *Py_UNUSED(ignored)) +longrange_iterator___length_hint___impl(longrangeiterobject *r) +/*[clinic end generated code: output=e1bce24da7e8bfde input=ba94b050d940411e]*/ { - longrangeiterobject *r = (longrangeiterobject*)op; Py_INCREF(r->len); return r->len; } +/*[clinic input] +@critical_section +longrange_iterator.__reduce__ + self as r: self(type="longrangeiterobject *") + +Return state information for pickling. +[clinic start generated code]*/ + static PyObject * -longrangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) +longrange_iterator___reduce___impl(longrangeiterobject *r) +/*[clinic end generated code: output=0077f94ae2a4e99a input=2e8930e897ace086]*/ { - longrangeiterobject *r = (longrangeiterobject*)op; PyObject *product, *stop=NULL; PyObject *range; @@ -1039,15 +1086,25 @@ longrangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) range, Py_None); } +/*[clinic input] +@critical_section +longrange_iterator.__setstate__ + self as r: self(type="longrangeiterobject *") + state: object + / + +Set state information for unpickling. +[clinic start generated code]*/ + static PyObject * -longrangeiter_setstate(PyObject *op, PyObject *state) +longrange_iterator___setstate___impl(longrangeiterobject *r, PyObject *state) +/*[clinic end generated code: output=870787f0574f0da4 input=8b116de3018de824]*/ { if (!PyLong_CheckExact(state)) { PyErr_Format(PyExc_TypeError, "state must be an int, not %T", state); return NULL; } - longrangeiterobject *r = (longrangeiterobject*)op; PyObject *zero = _PyLong_GetZero(); // borrowed reference int cmp; @@ -1085,9 +1142,9 @@ longrangeiter_setstate(PyObject *op, PyObject *state) } static PyMethodDef longrangeiter_methods[] = { - {"__length_hint__", longrangeiter_len, METH_NOARGS, length_hint_doc}, - {"__reduce__", longrangeiter_reduce, METH_NOARGS, reduce_doc}, - {"__setstate__", longrangeiter_setstate, METH_O, setstate_doc}, + LONGRANGE_ITERATOR___LENGTH_HINT___METHODDEF + LONGRANGE_ITERATOR___REDUCE___METHODDEF + LONGRANGE_ITERATOR___SETSTATE___METHODDEF {NULL, NULL} /* sentinel */ }; @@ -1102,7 +1159,7 @@ longrangeiter_dealloc(PyObject *op) } static PyObject * -longrangeiter_next(PyObject *op) +longrangeiter_next_lock_held(PyObject *op) { longrangeiterobject *r = (longrangeiterobject*)op; if (PyObject_RichCompareBool(r->len, _PyLong_GetZero(), Py_GT) != 1) @@ -1123,6 +1180,16 @@ longrangeiter_next(PyObject *op) return result; } +static PyObject * +longrangeiter_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = longrangeiter_next_lock_held(op); + Py_END_CRITICAL_SECTION(); + return result; +} + PyTypeObject PyLongRangeIter_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "longrange_iterator", /* tp_name */ diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index 46489f5cd9d..adc85d631db 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -20,15 +20,9 @@ race_top:_PyObject_TryGetInstanceAttribute race_top:PyUnstable_InterpreterFrame_GetLine race_top:write_thread_id -# gh-129068: race on shared range iterators (test_free_threading.test_zip.ZipThreading.test_threading) -race_top:rangeiter_next - # https://gist.github.com/mpage/6962e8870606cfc960e159b407a0cb40 thread:pthread_create -# Range iteration is not thread-safe yet (issue #129068) -race_top:rangeiter_next - # List resizing happens through different paths ending in memcpy or memmove # (for efficiency), which will probably need to rewritten as explicit loops # of ptr-sized copies to be thread-safe. (Issue #129069) From 33d94abafd0c6707561493a6dd16a85ff9776f05 Mon Sep 17 00:00:00 2001 From: LloydZ <35182391+cocolato@users.noreply.github.com> Date: Fri, 19 Dec 2025 02:25:36 +0800 Subject: [PATCH 42/80] gh-134584: Eliminate redundant refcounting from _BINARY_OP_SUBSCR_LIST_INT (GH-142926) --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_ids.h | 2 +- Include/internal/pycore_uop_metadata.h | 6 ++--- Lib/test/test_capi/test_opt.py | 18 +++++++++++++++ Python/bytecodes.c | 8 ++++--- Python/executor_cases.c.h | 26 ++++++++------------- Python/generated_cases.c.h | 28 +++++++++++++++-------- Python/optimizer_bytecodes.c | 6 +++++ Python/optimizer_cases.c.h | 16 ++++++++++--- 9 files changed, 74 insertions(+), 38 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 906ea0db0a5..913359ee4bd 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1338,7 +1338,7 @@ _PyOpcode_macro_expansion[256] = { [BINARY_OP_MULTIPLY_INT] = { .nuops = 5, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_INT, OPARG_SIMPLE, 0 }, { _BINARY_OP_MULTIPLY_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBSCR_DICT] = { .nuops = 2, .uops = { { _GUARD_NOS_DICT, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_DICT, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBSCR_GETITEM] = { .nuops = 4, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 5 }, { _BINARY_OP_SUBSCR_CHECK_FUNC, OPARG_SIMPLE, 5 }, { _BINARY_OP_SUBSCR_INIT_CALL, OPARG_SIMPLE, 5 }, { _PUSH_FRAME, OPARG_SIMPLE, 5 } } }, - [BINARY_OP_SUBSCR_LIST_INT] = { .nuops = 3, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_LIST, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_LIST_INT, OPARG_SIMPLE, 5 } } }, + [BINARY_OP_SUBSCR_LIST_INT] = { .nuops = 5, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_LIST, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_LIST_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 }, { _POP_TOP, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBSCR_LIST_SLICE] = { .nuops = 3, .uops = { { _GUARD_TOS_SLICE, OPARG_SIMPLE, 0 }, { _GUARD_NOS_LIST, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_LIST_SLICE, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBSCR_STR_INT] = { .nuops = 3, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_UNICODE, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_STR_INT, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBSCR_TUPLE_INT] = { .nuops = 3, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_TUPLE, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_TUPLE_INT, OPARG_SIMPLE, 5 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index f73aad9de1c..3b7b17b5d3e 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -382,7 +382,7 @@ extern "C" { #define _BINARY_OP_SUBSCR_INIT_CALL_r11 575 #define _BINARY_OP_SUBSCR_INIT_CALL_r21 576 #define _BINARY_OP_SUBSCR_INIT_CALL_r31 577 -#define _BINARY_OP_SUBSCR_LIST_INT_r21 578 +#define _BINARY_OP_SUBSCR_LIST_INT_r23 578 #define _BINARY_OP_SUBSCR_LIST_SLICE_r21 579 #define _BINARY_OP_SUBSCR_STR_INT_r21 580 #define _BINARY_OP_SUBSCR_TUPLE_INT_r21 581 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 0cc38919565..99ade22c0f4 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -1108,7 +1108,7 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { .entries = { { -1, -1, -1 }, { -1, -1, -1 }, - { 1, 2, _BINARY_OP_SUBSCR_LIST_INT_r21 }, + { 3, 2, _BINARY_OP_SUBSCR_LIST_INT_r23 }, { -1, -1, -1 }, }, }, @@ -3422,7 +3422,7 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_BINARY_OP_EXTEND_r21] = _BINARY_OP_EXTEND, [_BINARY_SLICE_r31] = _BINARY_SLICE, [_STORE_SLICE_r30] = _STORE_SLICE, - [_BINARY_OP_SUBSCR_LIST_INT_r21] = _BINARY_OP_SUBSCR_LIST_INT, + [_BINARY_OP_SUBSCR_LIST_INT_r23] = _BINARY_OP_SUBSCR_LIST_INT, [_BINARY_OP_SUBSCR_LIST_SLICE_r21] = _BINARY_OP_SUBSCR_LIST_SLICE, [_BINARY_OP_SUBSCR_STR_INT_r21] = _BINARY_OP_SUBSCR_STR_INT, [_GUARD_NOS_TUPLE_r02] = _GUARD_NOS_TUPLE, @@ -3930,7 +3930,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_BINARY_OP_SUBSCR_INIT_CALL_r21] = "_BINARY_OP_SUBSCR_INIT_CALL_r21", [_BINARY_OP_SUBSCR_INIT_CALL_r31] = "_BINARY_OP_SUBSCR_INIT_CALL_r31", [_BINARY_OP_SUBSCR_LIST_INT] = "_BINARY_OP_SUBSCR_LIST_INT", - [_BINARY_OP_SUBSCR_LIST_INT_r21] = "_BINARY_OP_SUBSCR_LIST_INT_r21", + [_BINARY_OP_SUBSCR_LIST_INT_r23] = "_BINARY_OP_SUBSCR_LIST_INT_r23", [_BINARY_OP_SUBSCR_LIST_SLICE] = "_BINARY_OP_SUBSCR_LIST_SLICE", [_BINARY_OP_SUBSCR_LIST_SLICE_r21] = "_BINARY_OP_SUBSCR_LIST_SLICE_r21", [_BINARY_OP_SUBSCR_STR_INT] = "_BINARY_OP_SUBSCR_STR_INT", diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 6f1b115b312..a35dea8557d 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2988,6 +2988,24 @@ class TestUopsOptimization(unittest.TestCase): for _ in range(TIER2_THRESHOLD+1): obj.attr = EvilAttr(obj.__dict__) + def test_binary_subscr_list_int(self): + def testfunc(n): + l = [1] + x = 0 + for _ in range(n): + y = l[0] + x += y + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_BINARY_OP_SUBSCR_LIST_INT", uops) + self.assertNotIn("_POP_TOP", uops) + self.assertNotIn("_POP_TOP_INT", uops) + self.assertIn("_POP_TOP_NOP", uops) def global_identity(x): return x diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 3e3b3d2f446..1c6d0bb9cdf 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -893,9 +893,9 @@ dummy_func( macro(STORE_SLICE) = _SPECIALIZE_STORE_SLICE + _STORE_SLICE; macro(BINARY_OP_SUBSCR_LIST_INT) = - _GUARD_TOS_INT + _GUARD_NOS_LIST + unused/5 + _BINARY_OP_SUBSCR_LIST_INT; + _GUARD_TOS_INT + _GUARD_NOS_LIST + unused/5 + _BINARY_OP_SUBSCR_LIST_INT + _POP_TOP_INT + POP_TOP; - op(_BINARY_OP_SUBSCR_LIST_INT, (list_st, sub_st -- res)) { + op(_BINARY_OP_SUBSCR_LIST_INT, (list_st, sub_st -- res, ls, ss)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); @@ -918,7 +918,9 @@ dummy_func( res = PyStackRef_FromPyObjectNew(res_o); #endif STAT_INC(BINARY_OP, hit); - DECREF_INPUTS(); + ls = list_st; + ss = sub_st; + INPUTS_DEAD(); } macro(BINARY_OP_SUBSCR_LIST_SLICE) = diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index b7ee752cd44..36ae739e58d 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4544,12 +4544,14 @@ break; } - case _BINARY_OP_SUBSCR_LIST_INT_r21: { + case _BINARY_OP_SUBSCR_LIST_INT_r23: { CHECK_CURRENT_CACHED_VALUES(2); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef sub_st; _PyStackRef list_st; _PyStackRef res; + _PyStackRef ls; + _PyStackRef ss; _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; sub_st = _stack_item_1; @@ -4600,23 +4602,13 @@ stack_pointer += 2; #endif STAT_INC(BINARY_OP, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = list_st; - list_st = res; - stack_pointer[-2] = list_st; - PyStackRef_CLOSE(tmp); - tmp = sub_st; - sub_st = PyStackRef_NULL; - stack_pointer[-1] = sub_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ls = list_st; + ss = sub_st; + _tos_cache2 = ss; + _tos_cache1 = ls; _tos_cache0 = res; - _tos_cache1 = PyStackRef_ZERO_BITS; - _tos_cache2 = PyStackRef_ZERO_BITS; - SET_CURRENT_CACHED_VALUES(1); - stack_pointer += -1; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; 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 28dc7b25f0d..0c0256bcc3b 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -764,6 +764,8 @@ _PyStackRef list_st; _PyStackRef sub_st; _PyStackRef res; + _PyStackRef ls; + _PyStackRef ss; // _GUARD_TOS_INT { value = stack_pointer[-1]; @@ -822,18 +824,24 @@ res = PyStackRef_FromPyObjectNew(res_o); #endif STAT_INC(BINARY_OP, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = list_st; - list_st = res; - stack_pointer[-2] = list_st; - PyStackRef_CLOSE(tmp); - tmp = sub_st; - sub_st = PyStackRef_NULL; - stack_pointer[-1] = sub_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + ls = list_st; + ss = sub_st; + } + // _POP_TOP_INT + { + value = ss; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP + { + value = ls; + stack_pointer[-2] = res; stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } DISPATCH(); } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index e3921054f7c..9578b56e707 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -1417,6 +1417,12 @@ dummy_func(void) { } } + op(_BINARY_OP_SUBSCR_LIST_INT, (list_st, sub_st -- res, ls, ss)) { + res = sym_new_unknown(ctx); + ls = list_st; + ss = sub_st; + } + // END BYTECODES // diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 32d60c76a4f..f047cd82d4c 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -763,11 +763,21 @@ } case _BINARY_OP_SUBSCR_LIST_INT: { + JitOptRef sub_st; + JitOptRef list_st; JitOptRef res; - res = sym_new_not_null(ctx); - CHECK_STACK_BOUNDS(-1); + JitOptRef ls; + JitOptRef ss; + sub_st = stack_pointer[-1]; + list_st = stack_pointer[-2]; + res = sym_new_unknown(ctx); + ls = list_st; + ss = sub_st; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[-1] = ls; + stack_pointer[0] = ss; + stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } From e79c39101a9f55882f54df0bb3ecfa23238692de Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Thu, 18 Dec 2025 10:31:37 -0800 Subject: [PATCH 43/80] gh-118342: [Enum] update docs (GH-137290) Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- Doc/library/enum.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index a8a7e671aad..0da27ba8e78 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -947,12 +947,13 @@ Utilities and Decorators the member's name. Care must be taken if mixing *auto()* with manually specified values. - *auto* instances are only resolved when at the top level of an assignment: + *auto* instances are only resolved when at the top level of an assignment, either by + itself or as part of a tuple: * ``FIRST = auto()`` will work (auto() is replaced with ``1``); * ``SECOND = auto(), -2`` will work (auto is replaced with ``2``, so ``2, -2`` is used to create the ``SECOND`` enum member; - * ``THREE = [auto(), -3]`` will *not* work (``, -3`` is used to + * ``THREE = [auto(), -3]`` will *not* work (``[, -3]`` is used to create the ``THREE`` enum member) .. versionchanged:: 3.11.1 From 1391ee664c8f019996e0cbf658e12fa38abd211d Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Thu, 18 Dec 2025 13:29:54 -0800 Subject: [PATCH 44/80] GH-134584: Remove redundant refcount for `BINARY_OP_SUBSCR_STR_INT` (#142844) --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_ids.h | 2 +- Include/internal/pycore_uop_metadata.h | 8 +++---- Lib/test/test_capi/test_opt.py | 20 ++++++++++++++++ Python/bytecodes.c | 10 ++++---- Python/executor_cases.c.h | 16 ++++++------- Python/generated_cases.c.h | 29 ++++++++++++++++------- Python/optimizer_bytecodes.c | 4 +++- Python/optimizer_cases.c.h | 14 +++++++++-- 9 files changed, 74 insertions(+), 31 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 913359ee4bd..92423fe5140 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1340,7 +1340,7 @@ _PyOpcode_macro_expansion[256] = { [BINARY_OP_SUBSCR_GETITEM] = { .nuops = 4, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 5 }, { _BINARY_OP_SUBSCR_CHECK_FUNC, OPARG_SIMPLE, 5 }, { _BINARY_OP_SUBSCR_INIT_CALL, OPARG_SIMPLE, 5 }, { _PUSH_FRAME, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBSCR_LIST_INT] = { .nuops = 5, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_LIST, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_LIST_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 }, { _POP_TOP, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBSCR_LIST_SLICE] = { .nuops = 3, .uops = { { _GUARD_TOS_SLICE, OPARG_SIMPLE, 0 }, { _GUARD_NOS_LIST, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_LIST_SLICE, OPARG_SIMPLE, 5 } } }, - [BINARY_OP_SUBSCR_STR_INT] = { .nuops = 3, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_UNICODE, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_STR_INT, OPARG_SIMPLE, 5 } } }, + [BINARY_OP_SUBSCR_STR_INT] = { .nuops = 5, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_UNICODE, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_STR_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 }, { _POP_TOP, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBSCR_TUPLE_INT] = { .nuops = 3, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_TUPLE, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBSCR_TUPLE_INT, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBTRACT_FLOAT] = { .nuops = 5, .uops = { { _GUARD_TOS_FLOAT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_FLOAT, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBTRACT_FLOAT, OPARG_SIMPLE, 5 }, { _POP_TOP_FLOAT, OPARG_SIMPLE, 5 }, { _POP_TOP_FLOAT, OPARG_SIMPLE, 5 } } }, [BINARY_OP_SUBTRACT_INT] = { .nuops = 5, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_INT, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBTRACT_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 }, { _POP_TOP_INT, OPARG_SIMPLE, 5 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 3b7b17b5d3e..e9851b767bc 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -384,7 +384,7 @@ extern "C" { #define _BINARY_OP_SUBSCR_INIT_CALL_r31 577 #define _BINARY_OP_SUBSCR_LIST_INT_r23 578 #define _BINARY_OP_SUBSCR_LIST_SLICE_r21 579 -#define _BINARY_OP_SUBSCR_STR_INT_r21 580 +#define _BINARY_OP_SUBSCR_STR_INT_r23 580 #define _BINARY_OP_SUBSCR_TUPLE_INT_r21 581 #define _BINARY_OP_SUBTRACT_FLOAT_r03 582 #define _BINARY_OP_SUBTRACT_FLOAT_r13 583 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 99ade22c0f4..3a6bf7b3d76 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -119,7 +119,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = { [_STORE_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_OP_SUBSCR_LIST_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_BINARY_OP_SUBSCR_LIST_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_BINARY_OP_SUBSCR_STR_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_OP_SUBSCR_STR_INT] = HAS_DEOPT_FLAG, [_GUARD_NOS_TUPLE] = HAS_EXIT_FLAG, [_GUARD_TOS_TUPLE] = HAS_EXIT_FLAG, [_BINARY_OP_SUBSCR_TUPLE_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, @@ -1126,7 +1126,7 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { .entries = { { -1, -1, -1 }, { -1, -1, -1 }, - { 1, 2, _BINARY_OP_SUBSCR_STR_INT_r21 }, + { 3, 2, _BINARY_OP_SUBSCR_STR_INT_r23 }, { -1, -1, -1 }, }, }, @@ -3424,7 +3424,7 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_STORE_SLICE_r30] = _STORE_SLICE, [_BINARY_OP_SUBSCR_LIST_INT_r23] = _BINARY_OP_SUBSCR_LIST_INT, [_BINARY_OP_SUBSCR_LIST_SLICE_r21] = _BINARY_OP_SUBSCR_LIST_SLICE, - [_BINARY_OP_SUBSCR_STR_INT_r21] = _BINARY_OP_SUBSCR_STR_INT, + [_BINARY_OP_SUBSCR_STR_INT_r23] = _BINARY_OP_SUBSCR_STR_INT, [_GUARD_NOS_TUPLE_r02] = _GUARD_NOS_TUPLE, [_GUARD_NOS_TUPLE_r12] = _GUARD_NOS_TUPLE, [_GUARD_NOS_TUPLE_r22] = _GUARD_NOS_TUPLE, @@ -3934,7 +3934,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_BINARY_OP_SUBSCR_LIST_SLICE] = "_BINARY_OP_SUBSCR_LIST_SLICE", [_BINARY_OP_SUBSCR_LIST_SLICE_r21] = "_BINARY_OP_SUBSCR_LIST_SLICE_r21", [_BINARY_OP_SUBSCR_STR_INT] = "_BINARY_OP_SUBSCR_STR_INT", - [_BINARY_OP_SUBSCR_STR_INT_r21] = "_BINARY_OP_SUBSCR_STR_INT_r21", + [_BINARY_OP_SUBSCR_STR_INT_r23] = "_BINARY_OP_SUBSCR_STR_INT_r23", [_BINARY_OP_SUBSCR_TUPLE_INT] = "_BINARY_OP_SUBSCR_TUPLE_INT", [_BINARY_OP_SUBSCR_TUPLE_INT_r21] = "_BINARY_OP_SUBSCR_TUPLE_INT_r21", [_BINARY_OP_SUBTRACT_FLOAT] = "_BINARY_OP_SUBTRACT_FLOAT", diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index a35dea8557d..88ac1664fe5 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1877,6 +1877,26 @@ class TestUopsOptimization(unittest.TestCase): self.assertNotIn("_GUARD_TOS_UNICODE", uops) self.assertIn("_BINARY_OP_ADD_UNICODE", uops) + def test_binary_op_subscr_str_int(self): + def testfunc(n): + x = 0 + s = "hello" + for _ in range(n): + c = s[1] # _BINARY_OP_SUBSCR_STR_INT + if c == 'e': + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBSCR_STR_INT", uops) + self.assertIn("_COMPARE_OP_STR", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertNotIn("_POP_TOP", uops) + self.assertNotIn("_POP_TOP_INT", uops) + def test_call_type_1_guards_removed(self): def testfunc(n): x = 0 diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 1c6d0bb9cdf..4ce7968ddc2 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -941,9 +941,9 @@ dummy_func( } macro(BINARY_OP_SUBSCR_STR_INT) = - _GUARD_TOS_INT + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_SUBSCR_STR_INT; + _GUARD_TOS_INT + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_SUBSCR_STR_INT + _POP_TOP_INT + POP_TOP; - op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res)) { + op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res, s, i)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); @@ -958,9 +958,9 @@ dummy_func( assert(c < 128); STAT_INC(BINARY_OP, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - DEAD(sub_st); - PyStackRef_CLOSE(str_st); + INPUTS_DEAD(); + s = str_st; + i = sub_st; res = PyStackRef_FromPyObjectBorrow(res_o); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 36ae739e58d..74bd939c87d 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4661,12 +4661,14 @@ break; } - case _BINARY_OP_SUBSCR_STR_INT_r21: { + case _BINARY_OP_SUBSCR_STR_INT_r23: { CHECK_CURRENT_CACHED_VALUES(2); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef sub_st; _PyStackRef str_st; _PyStackRef res; + _PyStackRef s; + _PyStackRef i; _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; sub_st = _stack_item_1; @@ -4701,15 +4703,13 @@ assert(c < 128); STAT_INC(BINARY_OP, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(str_st); - stack_pointer = _PyFrame_GetStackPointer(frame); + s = str_st; + i = sub_st; res = PyStackRef_FromPyObjectBorrow(res_o); + _tos_cache2 = i; + _tos_cache1 = s; _tos_cache0 = res; - _tos_cache1 = PyStackRef_ZERO_BITS; - _tos_cache2 = PyStackRef_ZERO_BITS; - SET_CURRENT_CACHED_VALUES(1); + SET_CURRENT_CACHED_VALUES(3); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 0c0256bcc3b..4482bb08a13 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -934,6 +934,8 @@ _PyStackRef str_st; _PyStackRef sub_st; _PyStackRef res; + _PyStackRef s; + _PyStackRef i; // _GUARD_TOS_INT { value = stack_pointer[-1]; @@ -983,17 +985,26 @@ assert(c < 128); STAT_INC(BINARY_OP, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - stack_pointer += -2; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(str_st); - stack_pointer = _PyFrame_GetStackPointer(frame); + s = str_st; + i = sub_st; res = PyStackRef_FromPyObjectBorrow(res_o); } - stack_pointer[0] = res; - stack_pointer += 1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + // _POP_TOP_INT + { + value = i; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP + { + value = s; + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } DISPATCH(); } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 9578b56e707..f36139db954 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -329,8 +329,10 @@ dummy_func(void) { ctx->done = true; } - op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res)) { + op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res, s, i)) { res = sym_new_type(ctx, &PyUnicode_Type); + s = str_st; + i = sub_st; } op(_BINARY_OP_SUBSCR_TUPLE_INT, (tuple_st, sub_st -- res)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index f047cd82d4c..69a2a32e4f1 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -793,11 +793,21 @@ } case _BINARY_OP_SUBSCR_STR_INT: { + JitOptRef sub_st; + JitOptRef str_st; JitOptRef res; + JitOptRef s; + JitOptRef i; + sub_st = stack_pointer[-1]; + str_st = stack_pointer[-2]; res = sym_new_type(ctx, &PyUnicode_Type); - CHECK_STACK_BOUNDS(-1); + s = str_st; + i = sub_st; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[-1] = s; + stack_pointer[0] = i; + stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } From 220f0b107776391201a399c54dd01692c36fcdf4 Mon Sep 17 00:00:00 2001 From: wangxiaolei Date: Fri, 19 Dec 2025 16:02:23 +0800 Subject: [PATCH 45/80] gh-142560: prevent use-after-free in search-like methods by exporting buffer in bytearray (#142938) --- Lib/test/test_bytes.py | 31 +++++ ...-12-11-22-59-33.gh-issue-142560.GkJrkk.rst | 1 + Objects/bytearrayobject.c | 113 +++++++++++------- 3 files changed, 103 insertions(+), 42 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-11-22-59-33.gh-issue-142560.GkJrkk.rst diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index a55ec6cf3b8..21be61e4fec 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -2060,6 +2060,37 @@ class ByteArrayTest(BaseBytesTest, unittest.TestCase): self.assertEqual(instance.ba[0], ord("?"), "Assigned bytearray not altered") self.assertEqual(instance.new_ba, bytearray(0x180), "Wrong object altered") + def test_search_methods_reentrancy_raises_buffererror(self): + # gh-142560: Raise BufferError if buffer mutates during search arg conversion. + class Evil: + def __init__(self, ba): + self.ba = ba + def __buffer__(self, flags): + self.ba.clear() + return memoryview(self.ba) + def __release_buffer__(self, view: memoryview) -> None: + view.release() + def __index__(self): + self.ba.clear() + return ord("A") + + def make_case(): + ba = bytearray(b"A") + return ba, Evil(ba) + + for name in ("find", "count", "index", "rindex", "rfind"): + ba, evil = make_case() + with self.subTest(name): + with self.assertRaises(BufferError): + getattr(ba, name)(evil) + + ba, evil = make_case() + with self.assertRaises(BufferError): + evil in ba + with self.assertRaises(BufferError): + ba.split(evil) + with self.assertRaises(BufferError): + ba.rsplit(evil) class AssortedBytesTest(unittest.TestCase): # diff --git a/Misc/NEWS.d/next/Library/2025-12-11-22-59-33.gh-issue-142560.GkJrkk.rst b/Misc/NEWS.d/next/Library/2025-12-11-22-59-33.gh-issue-142560.GkJrkk.rst new file mode 100644 index 00000000000..9c0657214b0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-11-22-59-33.gh-issue-142560.GkJrkk.rst @@ -0,0 +1 @@ +Fix use-after-free in :class:`bytearray` search-like methods (:meth:`~bytearray.find`, :meth:`~bytearray.count`, :meth:`~bytearray.index`, :meth:`~bytearray.rindex`, and :meth:`~bytearray.rfind`) by marking the storage as exported which causes reallocation attempts to raise :exc:`BufferError`. For :func:`~operator.contains`, :meth:`~bytearray.split`, and :meth:`~bytearray.rsplit` the :ref:`buffer protocol ` is used for this. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 25cc0bfcbab..338c71ad38f 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -90,6 +90,25 @@ bytearray_releasebuffer(PyObject *self, Py_buffer *view) Py_END_CRITICAL_SECTION(); } +typedef PyObject* (*_ba_bytes_op)(const char *buf, Py_ssize_t len, + PyObject *sub, Py_ssize_t start, + Py_ssize_t end); + +static PyObject * +_bytearray_with_buffer(PyByteArrayObject *self, _ba_bytes_op op, PyObject *sub, + Py_ssize_t start, Py_ssize_t end) +{ + PyObject *res; + + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); + + /* Increase exports to prevent bytearray storage from changing during op. */ + self->ob_exports++; + res = op(PyByteArray_AS_STRING(self), Py_SIZE(self), sub, start, end); + self->ob_exports--; + return res; +} + static int _canresize(PyByteArrayObject *self) { @@ -1248,8 +1267,7 @@ bytearray_find_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) /*[clinic end generated code: output=413e1cab2ae87da0 input=df3aa94840d893a7]*/ { - return _Py_bytes_find(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_find, sub, start, end); } /*[clinic input] @@ -1265,8 +1283,7 @@ bytearray_count_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) /*[clinic end generated code: output=a21ee2692e4f1233 input=e8fcdca8272857e0]*/ { - return _Py_bytes_count(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_count, sub, start, end); } /*[clinic input] @@ -1314,8 +1331,7 @@ bytearray_index_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) /*[clinic end generated code: output=067a1e78efc672a7 input=c37f177cfee19fe4]*/ { - return _Py_bytes_index(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_index, sub, start, end); } /*[clinic input] @@ -1333,8 +1349,7 @@ bytearray_rfind_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) /*[clinic end generated code: output=51bf886f932b283c input=1265b11c437d2750]*/ { - return _Py_bytes_rfind(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_rfind, sub, start, end); } /*[clinic input] @@ -1352,18 +1367,22 @@ bytearray_rindex_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) /*[clinic end generated code: output=38e1cf66bafb08b9 input=7d198b3d6b0a62ce]*/ { - return _Py_bytes_rindex(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_rindex, sub, start, end); } static int bytearray_contains(PyObject *self, PyObject *arg) { - int ret; + int ret = -1; Py_BEGIN_CRITICAL_SECTION(self); - ret = _Py_bytes_contains(PyByteArray_AS_STRING(self), + PyByteArrayObject *ba = _PyByteArray_CAST(self); + + /* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */ + ba->ob_exports++; + ret = _Py_bytes_contains(PyByteArray_AS_STRING(ba), PyByteArray_GET_SIZE(self), arg); + ba->ob_exports--; Py_END_CRITICAL_SECTION(); return ret; } @@ -1390,8 +1409,7 @@ bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj, Py_ssize_t start, Py_ssize_t end) /*[clinic end generated code: output=a3d9b6d44d3662a6 input=93f9ffee684f109a]*/ { - return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - subobj, start, end); + return _bytearray_with_buffer(self, _Py_bytes_startswith, subobj, start, end); } /*[clinic input] @@ -1416,8 +1434,7 @@ bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj, Py_ssize_t start, Py_ssize_t end) /*[clinic end generated code: output=e75ea8c227954caa input=d158b030a11d0b06]*/ { - return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - subobj, start, end); + return _bytearray_with_buffer(self, _Py_bytes_endswith, subobj, start, end); } /*[clinic input] @@ -1782,26 +1799,32 @@ bytearray_split_impl(PyByteArrayObject *self, PyObject *sep, Py_ssize_t maxsplit) /*[clinic end generated code: output=833e2cf385d9a04d input=dd9f6e2910cc3a34]*/ { - Py_ssize_t len = PyByteArray_GET_SIZE(self), n; - const char *s = PyByteArray_AS_STRING(self), *sub; - PyObject *list; - Py_buffer vsub; + PyObject *list = NULL; + + /* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */ + self->ob_exports++; + const char *sbuf = PyByteArray_AS_STRING(self); + Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self); if (maxsplit < 0) maxsplit = PY_SSIZE_T_MAX; - if (sep == Py_None) - return stringlib_split_whitespace((PyObject*) self, s, len, maxsplit); + if (sep == Py_None) { + list = stringlib_split_whitespace((PyObject*)self, sbuf, slen, maxsplit); + goto done; + } - if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) - return NULL; - sub = vsub.buf; - n = vsub.len; + Py_buffer vsub; + if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) { + goto done; + } - list = stringlib_split( - (PyObject*) self, s, len, sub, n, maxsplit - ); + list = stringlib_split((PyObject*)self, sbuf, slen, + (const char *)vsub.buf, vsub.len, maxsplit); PyBuffer_Release(&vsub); + +done: + self->ob_exports--; return list; } @@ -1900,26 +1923,32 @@ bytearray_rsplit_impl(PyByteArrayObject *self, PyObject *sep, Py_ssize_t maxsplit) /*[clinic end generated code: output=a55e0b5a03cb6190 input=60e9abf305128ff4]*/ { - Py_ssize_t len = PyByteArray_GET_SIZE(self), n; - const char *s = PyByteArray_AS_STRING(self), *sub; - PyObject *list; - Py_buffer vsub; + PyObject *list = NULL; + + /* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */ + self->ob_exports++; + const char *sbuf = PyByteArray_AS_STRING(self); + Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self); if (maxsplit < 0) maxsplit = PY_SSIZE_T_MAX; - if (sep == Py_None) - return stringlib_rsplit_whitespace((PyObject*) self, s, len, maxsplit); + if (sep == Py_None) { + list = stringlib_rsplit_whitespace((PyObject*)self, sbuf, slen, maxsplit); + goto done; + } - if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) - return NULL; - sub = vsub.buf; - n = vsub.len; + Py_buffer vsub; + if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) { + goto done; + } - list = stringlib_rsplit( - (PyObject*) self, s, len, sub, n, maxsplit - ); + list = stringlib_rsplit((PyObject*)self, sbuf, slen, + (const char *)vsub.buf, vsub.len, maxsplit); PyBuffer_Release(&vsub); + +done: + self->ob_exports--; return list; } From 610aabfef2f90dc1a274703779615aa5b7fbbb3a Mon Sep 17 00:00:00 2001 From: Andrej Date: Fri, 19 Dec 2025 00:29:35 -0800 Subject: [PATCH 46/80] gh-142527: Docs: Clarify that random.seed() discards the sign of an integer input (#142483) If *a* is an integer, the sign of *a* is discarded in the C source code. Clarify this behavior to prevent foot guns, where a common use case might naively assume that flipping the sign will produce different sequences (e.g. for a train/test split of a synthetic data generator in machine learning). Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/random.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 4e55e301b89..6bddf575a80 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -78,7 +78,7 @@ Bookkeeping functions instead of the system time (see the :func:`os.urandom` function for details on availability). - If *a* is an int, it is used directly. + If *a* is an int, its absolute value is used directly. With version 2 (the default), a :class:`str`, :class:`bytes`, or :class:`bytearray` object gets converted to an :class:`int` and all of its bits are used. From 4aef13832521b4e7785c9643f6a995c92b4a691d Mon Sep 17 00:00:00 2001 From: Rogdham <3994389+Rogdham@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:44:03 +0100 Subject: [PATCH 47/80] gh-136282: Configparser: create unnamed sections via mapping protocol access (GH-136313) --- Lib/configparser.py | 3 ++- Lib/test/test_configparser.py | 13 +++++++++++++ .../2025-07-05-08-30-07.gh-issue-136282.K3JKyD.rst | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-07-05-08-30-07.gh-issue-136282.K3JKyD.rst diff --git a/Lib/configparser.py b/Lib/configparser.py index 18af1eadaad..d435a5c2fe0 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -794,7 +794,8 @@ class RawConfigParser(MutableMapping): """ elements_added = set() for section, keys in dictionary.items(): - section = str(section) + if section is not UNNAMED_SECTION: + section = str(section) try: self.add_section(section) except (DuplicateSectionError, ValueError): diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index e7364e18742..1bfb53ccbb1 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -2215,6 +2215,16 @@ class SectionlessTestCase(unittest.TestCase): cfg.add_section(configparser.UNNAMED_SECTION) cfg.set(configparser.UNNAMED_SECTION, 'a', '1') self.assertEqual('1', cfg[configparser.UNNAMED_SECTION]['a']) + output = io.StringIO() + cfg.write(output) + self.assertEqual(output.getvalue(), 'a = 1\n\n') + + cfg = configparser.ConfigParser(allow_unnamed_section=True) + cfg[configparser.UNNAMED_SECTION] = {'a': '1'} + self.assertEqual('1', cfg[configparser.UNNAMED_SECTION]['a']) + output = io.StringIO() + cfg.write(output) + self.assertEqual(output.getvalue(), 'a = 1\n\n') def test_disabled_error(self): with self.assertRaises(configparser.MissingSectionHeaderError): @@ -2223,6 +2233,9 @@ class SectionlessTestCase(unittest.TestCase): with self.assertRaises(configparser.UnnamedSectionDisabledError): configparser.ConfigParser().add_section(configparser.UNNAMED_SECTION) + with self.assertRaises(configparser.UnnamedSectionDisabledError): + configparser.ConfigParser()[configparser.UNNAMED_SECTION] = {'a': '1'} + def test_multiple_configs(self): cfg = configparser.ConfigParser(allow_unnamed_section=True) cfg.read_string('a = 1') diff --git a/Misc/NEWS.d/next/Library/2025-07-05-08-30-07.gh-issue-136282.K3JKyD.rst b/Misc/NEWS.d/next/Library/2025-07-05-08-30-07.gh-issue-136282.K3JKyD.rst new file mode 100644 index 00000000000..b5589b47716 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-05-08-30-07.gh-issue-136282.K3JKyD.rst @@ -0,0 +1,2 @@ +Add support for :const:`~configparser.UNNAMED_SECTION` when creating a +section via the mapping protocol access From 685272eb8abb3b5ad73f6251f56990229f42456e Mon Sep 17 00:00:00 2001 From: Diego Russo Date: Fri, 19 Dec 2025 14:39:41 +0000 Subject: [PATCH 48/80] JIT: Rename trampoline.c to shim.c (#142974) --- Include/internal/pycore_ceval.h | 2 +- Python/ceval.c | 4 ++-- Python/jit.c | 18 +++++++++--------- Python/pystate.c | 2 +- Tools/jit/_targets.py | 4 ++-- Tools/jit/_writer.py | 4 ++-- Tools/jit/{trampoline.c => shim.c} | 0 7 files changed, 17 insertions(+), 17 deletions(-) rename Tools/jit/{trampoline.c => shim.c} (100%) diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index af53f2e7d6f..bf34c55cd78 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -123,7 +123,7 @@ _PyEval_EvalFrame(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwfl #ifdef _Py_TIER2 #ifdef _Py_JIT -_Py_CODEUNIT *_Py_LazyJitTrampoline( +_Py_CODEUNIT *_Py_LazyJitShim( struct _PyExecutorObject *current_executor, _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate ); diff --git a/Python/ceval.c b/Python/ceval.c index 90ae0b022e3..cf86d5484f0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1601,7 +1601,7 @@ early_exit: } #ifdef _Py_TIER2 #ifdef _Py_JIT -_PyJitEntryFuncPtr _Py_jit_entry = _Py_LazyJitTrampoline; +_PyJitEntryFuncPtr _Py_jit_entry = _Py_LazyJitShim; #else _PyJitEntryFuncPtr _Py_jit_entry = _PyTier2Interpreter; #endif @@ -1617,7 +1617,7 @@ _PyTier2Interpreter( const _PyUOpInstruction *next_uop; int oparg; /* Set up "jit" state after entry from tier 1. - * This mimics what the jit trampoline function does. */ + * This mimics what the jit shim function does. */ tstate->jit_exit = NULL; _PyStackRef _tos_cache0 = PyStackRef_ZERO_BITS; _PyStackRef _tos_cache1 = PyStackRef_ZERO_BITS; diff --git a/Python/jit.c b/Python/jit.c index 7660f6f9bea..4ce90edf73a 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -672,20 +672,20 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz return 0; } -/* One-off compilation of the jit entry trampoline +/* One-off compilation of the jit entry shim * We compile this once only as it effectively a normal * function, but we need to use the JIT because it needs * to understand the jit-specific calling convention. */ static _PyJitEntryFuncPtr -compile_trampoline(void) +compile_shim(void) { _PyExecutorObject dummy; const StencilGroup *group; size_t code_size = 0; size_t data_size = 0; jit_state state = {0}; - group = &trampoline; + group = &shim; code_size += group->code_size; data_size += group->data_size; combine_symbol_mask(group->trampoline_mask, state.trampolines.mask); @@ -707,7 +707,7 @@ compile_trampoline(void) // Compile the shim, which handles converting between the native // calling convention and the calling convention used by jitted code // (which may be different for efficiency reasons). - group = &trampoline; + group = &shim; group->emit(code, data, &dummy, NULL, &state); code += group->code_size; data += group->data_size; @@ -723,17 +723,17 @@ compile_trampoline(void) static PyMutex lazy_jit_mutex = { 0 }; _Py_CODEUNIT * -_Py_LazyJitTrampoline( +_Py_LazyJitShim( _PyExecutorObject *executor, _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate ) { PyMutex_Lock(&lazy_jit_mutex); - if (_Py_jit_entry == _Py_LazyJitTrampoline) { - _PyJitEntryFuncPtr trampoline = compile_trampoline(); - if (trampoline == NULL) { + if (_Py_jit_entry == _Py_LazyJitShim) { + _PyJitEntryFuncPtr shim = compile_shim(); + if (shim == NULL) { PyMutex_Unlock(&lazy_jit_mutex); Py_FatalError("Cannot allocate core JIT code"); } - _Py_jit_entry = trampoline; + _Py_jit_entry = shim; } PyMutex_Unlock(&lazy_jit_mutex); return _Py_jit_entry(executor, frame, stack_pointer, tstate); diff --git a/Python/pystate.c b/Python/pystate.c index 7ea8ef91f10..cf55297cf8d 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -490,7 +490,7 @@ static inline int check_interpreter_whence(long); #endif extern _Py_CODEUNIT * -_Py_LazyJitTrampoline( +_Py_LazyJitShim( struct _PyExecutorObject *exec, _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate ); diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 5895e91c3c4..39be353ec30 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -204,8 +204,8 @@ class _Target(typing.Generic[_S, _R]): with tempfile.TemporaryDirectory() as tempdir: work = pathlib.Path(tempdir).resolve() async with asyncio.TaskGroup() as group: - coro = self._compile("trampoline", TOOLS_JIT / "trampoline.c", work) - tasks.append(group.create_task(coro, name="trampoline")) + coro = self._compile("shim", TOOLS_JIT / "shim.c", work) + tasks.append(group.create_task(coro, name="shim")) template = TOOLS_JIT_TEMPLATE_C.read_text() for case, opname in cases_and_opnames: # Write out a copy of the template with *only* this case diff --git a/Tools/jit/_writer.py b/Tools/jit/_writer.py index 3a59ffce7a2..5fd9a2ee2d6 100644 --- a/Tools/jit/_writer.py +++ b/Tools/jit/_writer.py @@ -23,11 +23,11 @@ def _dump_footer( yield " symbol_mask got_mask;" yield "} StencilGroup;" yield "" - yield f"static const StencilGroup trampoline = {groups['trampoline'].as_c('trampoline')};" + yield f"static const StencilGroup shim = {groups['shim'].as_c('shim')};" yield "" yield "static const StencilGroup stencil_groups[MAX_UOP_REGS_ID + 1] = {" for opname, group in sorted(groups.items()): - if opname == "trampoline": + if opname == "shim": continue yield f" [{opname}] = {group.as_c(opname)}," yield "};" diff --git a/Tools/jit/trampoline.c b/Tools/jit/shim.c similarity index 100% rename from Tools/jit/trampoline.c rename to Tools/jit/shim.c From 049c2526bffddf94495b7c0d2d788ac9faf4078a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 19 Dec 2025 17:48:34 +0100 Subject: [PATCH 49/80] gh-134160: Start "Extending and embedding" with a Diataxis-style tutorial (GH-142314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Co-authored-by: Éric Co-authored-by: Daniele Nicolodi Co-authored-by: Peter Bierma --- Doc/c-api/intro.rst | 40 ++ Doc/extending/extending.rst | 421 ++---------- Doc/extending/first-extension-module.rst | 667 ++++++++++++++++++++ Doc/extending/index.rst | 86 ++- Doc/includes/capi-extension/spammodule-01.c | 55 ++ 5 files changed, 885 insertions(+), 384 deletions(-) create mode 100644 Doc/extending/first-extension-module.rst create mode 100644 Doc/includes/capi-extension/spammodule-01.c diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index bb94bcb86a7..5e90d9b7bc9 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -107,6 +107,46 @@ header files properly declare the entry points to be ``extern "C"``. As a result there is no need to do anything special to use the API from C++. +.. _capi-system-includes: + +System includes +--------------- + + :file:`Python.h` includes several standard header files. + C extensions should include the standard headers that they use, + and should not rely on these implicit includes. + The implicit includes are: + + * ```` + * ```` (on Windows) + * ```` + * ```` + * ```` + * ```` + * ```` + * ```` (if present) + + The following are included for backwards compatibility, unless using + :ref:`Limited API ` 3.13 or newer: + + * ```` + * ```` (on POSIX) + + The following are included for backwards compatibility, unless using + :ref:`Limited API ` 3.11 or newer: + + * ```` + * ```` + * ```` + * ```` + +.. note:: + + Since Python may define some pre-processor definitions which affect the standard + headers on some systems, you *must* include :file:`Python.h` before any standard + headers are included. + + Useful macros ============= diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index f9b65643dfe..c0066d315d0 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -3,154 +3,20 @@ .. _extending-intro: -****************************** -Extending Python with C or C++ -****************************** +******************************** +Using the C API: Assorted topics +******************************** -It is quite easy to add new built-in modules to Python, if you know how to -program in C. Such :dfn:`extension modules` can do two things that can't be -done directly in Python: they can implement new built-in object types, and they -can call C library functions and system calls. - -To support extensions, the Python API (Application Programmers Interface) -defines a set of functions, macros and variables that provide access to most -aspects of the Python run-time system. The Python API is incorporated in a C -source file by including the header ``"Python.h"``. - -The compilation of an extension module depends on its intended use as well as on -your system setup; details are given in later chapters. - -.. note:: - - The C extension interface is specific to CPython, and extension modules do - not work on other Python implementations. In many cases, it is possible to - avoid writing C extensions and preserve portability to other implementations. - For example, if your use case is calling C library functions or system calls, - you should consider using the :mod:`ctypes` module or the `cffi - `_ library rather than writing - custom C code. - These modules let you write Python code to interface with C code and are more - portable between implementations of Python than writing and compiling a C - extension module. - - -.. _extending-simpleexample: - -A Simple Example -================ - -Let's create an extension module called ``spam`` (the favorite food of Monty -Python fans...) and let's say we want to create a Python interface to the C -library function :c:func:`system` [#]_. This function takes a null-terminated -character string as argument and returns an integer. We want this function to -be callable from Python as follows: - -.. code-block:: pycon - - >>> import spam - >>> status = spam.system("ls -l") - -Begin by creating a file :file:`spammodule.c`. (Historically, if a module is -called ``spam``, the C file containing its implementation is called -:file:`spammodule.c`; if the module name is very long, like ``spammify``, the -module name can be just :file:`spammify.c`.) - -The first two lines of our file can be:: - - #define PY_SSIZE_T_CLEAN - #include - -which pulls in the Python API (you can add a comment describing the purpose of -the module and a copyright notice if you like). - -.. note:: - - Since Python may define some pre-processor definitions which affect the standard - headers on some systems, you *must* include :file:`Python.h` before any standard - headers are included. - - ``#define PY_SSIZE_T_CLEAN`` was used to indicate that ``Py_ssize_t`` should be - used in some APIs instead of ``int``. - It is not necessary since Python 3.13, but we keep it here for backward compatibility. - See :ref:`arg-parsing-string-and-buffers` for a description of this macro. - -All user-visible symbols defined by :file:`Python.h` have a prefix of ``Py`` or -``PY``, except those defined in standard header files. - -.. tip:: - - For backward compatibility, :file:`Python.h` includes several standard header files. - C extensions should include the standard headers that they use, - and should not rely on these implicit includes. - If using the limited C API version 3.13 or newer, the implicit includes are: - - * ```` - * ```` (on Windows) - * ```` - * ```` - * ```` - * ```` - * ```` - * ```` (if present) - - If :c:macro:`Py_LIMITED_API` is not defined, or is set to version 3.12 or older, - the headers below are also included: - - * ```` - * ```` (on POSIX) - - If :c:macro:`Py_LIMITED_API` is not defined, or is set to version 3.10 or older, - the headers below are also included: - - * ```` - * ```` - * ```` - * ```` - -The next thing we add to our module file is the C function that will be called -when the Python expression ``spam.system(string)`` is evaluated (we'll see -shortly how it ends up being called):: - - static PyObject * - spam_system(PyObject *self, PyObject *args) - { - const char *command; - int sts; - - if (!PyArg_ParseTuple(args, "s", &command)) - return NULL; - sts = system(command); - return PyLong_FromLong(sts); - } - -There is a straightforward translation from the argument list in Python (for -example, the single expression ``"ls -l"``) to the arguments passed to the C -function. The C function always has two arguments, conventionally named *self* -and *args*. - -The *self* argument points to the module object for module-level functions; -for a method it would point to the object instance. - -The *args* argument will be a pointer to a Python tuple object containing the -arguments. Each item of the tuple corresponds to an argument in the call's -argument list. The arguments are Python objects --- in order to do anything -with them in our C function we have to convert them to C values. The function -:c:func:`PyArg_ParseTuple` in the Python API checks the argument types and -converts them to C values. It uses a template string to determine the required -types of the arguments as well as the types of the C variables into which to -store the converted values. More about this later. - -:c:func:`PyArg_ParseTuple` returns true (nonzero) if all arguments have the right -type and its components have been stored in the variables whose addresses are -passed. It returns false (zero) if an invalid argument list was passed. In the -latter case it also raises an appropriate exception so the calling function can -return ``NULL`` immediately (as we saw in the example). +The :ref:`tutorial ` walked you through +creating a C API extension module, but left many areas unexplained. +This document looks at several concepts that you'll need to learn +in order to write more complex extensions. .. _extending-errors: -Intermezzo: Errors and Exceptions -================================= +Errors and Exceptions +===================== An important convention throughout the Python interpreter is the following: when a function fails, it should set an exception condition and return an error value @@ -321,194 +187,14 @@ call to :c:func:`PyErr_SetString` as shown below:: } -.. _backtoexample: - -Back to the Example -=================== - -Going back to our example function, you should now be able to understand this -statement:: - - if (!PyArg_ParseTuple(args, "s", &command)) - return NULL; - -It returns ``NULL`` (the error indicator for functions returning object pointers) -if an error is detected in the argument list, relying on the exception set by -:c:func:`PyArg_ParseTuple`. Otherwise the string value of the argument has been -copied to the local variable :c:data:`!command`. This is a pointer assignment and -you are not supposed to modify the string to which it points (so in Standard C, -the variable :c:data:`!command` should properly be declared as ``const char -*command``). - -The next statement is a call to the Unix function :c:func:`system`, passing it -the string we just got from :c:func:`PyArg_ParseTuple`:: - - sts = system(command); - -Our :func:`!spam.system` function must return the value of :c:data:`!sts` as a -Python object. This is done using the function :c:func:`PyLong_FromLong`. :: - - return PyLong_FromLong(sts); - -In this case, it will return an integer object. (Yes, even integers are objects -on the heap in Python!) - -If you have a C function that returns no useful argument (a function returning -:c:expr:`void`), the corresponding Python function must return ``None``. You -need this idiom to do so (which is implemented by the :c:macro:`Py_RETURN_NONE` -macro):: - - Py_INCREF(Py_None); - return Py_None; - -:c:data:`Py_None` is the C name for the special Python object ``None``. It is a -genuine Python object rather than a ``NULL`` pointer, which means "error" in most -contexts, as we have seen. - - -.. _methodtable: - -The Module's Method Table and Initialization Function -===================================================== - -I promised to show how :c:func:`!spam_system` is called from Python programs. -First, we need to list its name and address in a "method table":: - - static PyMethodDef spam_methods[] = { - ... - {"system", spam_system, METH_VARARGS, - "Execute a shell command."}, - ... - {NULL, NULL, 0, NULL} /* Sentinel */ - }; - -Note the third entry (``METH_VARARGS``). This is a flag telling the interpreter -the calling convention to be used for the C function. It should normally always -be ``METH_VARARGS`` or ``METH_VARARGS | METH_KEYWORDS``; a value of ``0`` means -that an obsolete variant of :c:func:`PyArg_ParseTuple` is used. - -When using only ``METH_VARARGS``, the function should expect the Python-level -parameters to be passed in as a tuple acceptable for parsing via -:c:func:`PyArg_ParseTuple`; more information on this function is provided below. - -The :c:macro:`METH_KEYWORDS` bit may be set in the third field if keyword -arguments should be passed to the function. In this case, the C function should -accept a third ``PyObject *`` parameter which will be a dictionary of keywords. -Use :c:func:`PyArg_ParseTupleAndKeywords` to parse the arguments to such a -function. - -The method table must be referenced in the module definition structure:: - - static struct PyModuleDef spam_module = { - ... - .m_methods = spam_methods, - ... - }; - -This structure, in turn, must be passed to the interpreter in the module's -initialization function. The initialization function must be named -:c:func:`!PyInit_name`, where *name* is the name of the module, and should be the -only non-\ ``static`` item defined in the module file:: - - PyMODINIT_FUNC - PyInit_spam(void) - { - return PyModuleDef_Init(&spam_module); - } - -Note that :c:macro:`PyMODINIT_FUNC` declares the function as ``PyObject *`` return type, -declares any special linkage declarations required by the platform, and for C++ -declares the function as ``extern "C"``. - -:c:func:`!PyInit_spam` is called when each interpreter imports its module -:mod:`!spam` for the first time. (See below for comments about embedding Python.) -A pointer to the module definition must be returned via :c:func:`PyModuleDef_Init`, -so that the import machinery can create the module and store it in ``sys.modules``. - -When embedding Python, the :c:func:`!PyInit_spam` function is not called -automatically unless there's an entry in the :c:data:`PyImport_Inittab` table. -To add the module to the initialization table, use :c:func:`PyImport_AppendInittab`, -optionally followed by an import of the module:: - - #define PY_SSIZE_T_CLEAN - #include - - int - main(int argc, char *argv[]) - { - PyStatus status; - PyConfig config; - PyConfig_InitPythonConfig(&config); - - /* Add a built-in module, before Py_Initialize */ - if (PyImport_AppendInittab("spam", PyInit_spam) == -1) { - fprintf(stderr, "Error: could not extend in-built modules table\n"); - exit(1); - } - - /* Pass argv[0] to the Python interpreter */ - status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]); - if (PyStatus_Exception(status)) { - goto exception; - } - - /* Initialize the Python interpreter. Required. - If this step fails, it will be a fatal error. */ - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - goto exception; - } - PyConfig_Clear(&config); - - /* Optionally import the module; alternatively, - import can be deferred until the embedded script - imports it. */ - PyObject *pmodule = PyImport_ImportModule("spam"); - if (!pmodule) { - PyErr_Print(); - fprintf(stderr, "Error: could not import module 'spam'\n"); - } - - // ... use Python C API here ... - - return 0; - - exception: - PyConfig_Clear(&config); - Py_ExitStatusException(status); - } - -.. note:: - - If you declare a global variable or a local static one, the module may - experience unintended side-effects on re-initialisation, for example when - removing entries from ``sys.modules`` or importing compiled modules into - multiple interpreters within a process - (or following a :c:func:`fork` without an intervening :c:func:`exec`). - If module state is not yet fully :ref:`isolated `, - authors should consider marking the module as having no support for subinterpreters - (via :c:macro:`Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED`). - -A more substantial example module is included in the Python source distribution -as :file:`Modules/xxlimited.c`. This file may be used as a template or simply -read as an example. - - .. _compilation: -Compilation and Linkage -======================= +Embedding an extension +====================== -There are two more things to do before you can use your new extension: compiling -and linking it with the Python system. If you use dynamic loading, the details -may depend on the style of dynamic loading your system uses; see the chapters -about building extension modules (chapter :ref:`building`) and additional -information that pertains only to building on Windows (chapter -:ref:`building-on-windows`) for more information about this. - -If you can't use dynamic loading, or if you want to make your module a permanent +If you want to make your module a permanent part of the Python interpreter, you will have to change the configuration setup -and rebuild the interpreter. Luckily, this is very simple on Unix: just place +and rebuild the interpreter. On Unix, place your file (:file:`spammodule.c` for example) in the :file:`Modules/` directory of an unpacked source distribution, add a line to the file :file:`Modules/Setup.local` describing your file: @@ -536,7 +222,7 @@ on the line in the configuration file as well, for instance: Calling Python Functions from C =============================== -So far we have concentrated on making C functions callable from Python. The +The tutorial concentrated on making C functions callable from Python. The reverse is also useful: calling Python functions from C. This is especially the case for libraries that support so-called "callback" functions. If a C interface makes use of callbacks, the equivalent Python often needs to provide a @@ -581,7 +267,7 @@ be part of a module definition:: } This function must be registered with the interpreter using the -:c:macro:`METH_VARARGS` flag; this is described in section :ref:`methodtable`. The +:c:macro:`METH_VARARGS` flag in :c:type:`PyMethodDef.ml_flags`. The :c:func:`PyArg_ParseTuple` function and its arguments are documented in section :ref:`parsetuple`. @@ -676,14 +362,21 @@ the above example, we use :c:func:`Py_BuildValue` to construct the dictionary. : Py_DECREF(result); +.. index:: single: PyArg_ParseTuple (C function) + .. _parsetuple: Extracting Parameters in Extension Functions ============================================ -.. index:: single: PyArg_ParseTuple (C function) +The :ref:`tutorial ` uses a ":c:data:`METH_O`" +function, which is limited to a single Python argument. +If you want more, you can use :c:data:`METH_VARARGS` instead. +With this flag, the C function will receive a :py:class:`tuple` of arguments +instead of a single object. -The :c:func:`PyArg_ParseTuple` function is declared as follows:: +For unpacking the tuple, CPython provides the :c:func:`PyArg_ParseTuple` +function, declared as follows:: int PyArg_ParseTuple(PyObject *arg, const char *format, ...); @@ -693,6 +386,19 @@ whose syntax is explained in :ref:`arg-parsing` in the Python/C API Reference Manual. The remaining arguments must be addresses of variables whose type is determined by the format string. +For example, to receive a single Python :py:class:`str` object and turn it +into a C buffer, you would use ``"s"`` as the format string:: + + const char *command; + if (!PyArg_ParseTuple(args, "s", &command)) { + return NULL; + } + +If an error is detected in the argument list, :c:func:`!PyArg_ParseTuple` +returns ``NULL`` (the error indicator for functions returning object pointers); +your function may return ``NULL``, relying on the exception set by +:c:func:`PyArg_ParseTuple`. + Note that while :c:func:`PyArg_ParseTuple` checks that the Python arguments have the required types, it cannot check the validity of the addresses of C variables passed to the call: if you make mistakes there, your code will probably crash or @@ -703,7 +409,6 @@ Note that any Python object references which are provided to the caller are Some example calls:: - #define PY_SSIZE_T_CLEAN #include :: @@ -773,6 +478,17 @@ Some example calls:: Keyword Parameters for Extension Functions ========================================== +If you also want your function to accept +:term:`keyword arguments `, use the :c:data:`METH_KEYWORDS` +flag in combination with :c:data:`METH_VARARGS`. +(:c:data:`!METH_KEYWORDS` can also be used with other flags; see its +documentation for the allowed combinations.) + +In this case, the C function should accept a third ``PyObject *`` parameter +which will be a dictionary of keywords. +Use :c:func:`PyArg_ParseTupleAndKeywords` to parse the arguments to such a +function. + .. index:: single: PyArg_ParseTupleAndKeywords (C function) The :c:func:`PyArg_ParseTupleAndKeywords` function is declared as follows:: @@ -833,19 +549,6 @@ Philbrick (philbrick@hks.com):: {NULL, NULL, 0, NULL} /* sentinel */ }; - static struct PyModuleDef keywdarg_module = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = "keywdarg", - .m_size = 0, - .m_methods = keywdarg_methods, - }; - - PyMODINIT_FUNC - PyInit_keywdarg(void) - { - return PyModuleDef_Init(&keywdarg_module); - } - .. _buildvalue: @@ -986,11 +689,11 @@ needed. Ownership of a reference can be transferred. There are three ways to dispose of an owned reference: pass it on, store it, or call :c:func:`Py_DECREF`. Forgetting to dispose of an owned reference creates a memory leak. -It is also possible to :dfn:`borrow` [#]_ a reference to an object. The +It is also possible to :dfn:`borrow` [#borrow]_ a reference to an object. The borrower of a reference should not call :c:func:`Py_DECREF`. The borrower must not hold on to the object longer than the owner from which it was borrowed. Using a borrowed reference after the owner has disposed of it risks using freed -memory and should be avoided completely [#]_. +memory and should be avoided completely [#dont-check-refcount]_. The advantage of borrowing over owning a reference is that you don't need to take care of disposing of the reference on all possible paths through the code @@ -1169,7 +872,7 @@ checking. The C function calling mechanism guarantees that the argument list passed to C functions (``args`` in the examples) is never ``NULL`` --- in fact it guarantees -that it is always a tuple [#]_. +that it is always a tuple [#old-calling-convention]_. It is a severe error to ever let a ``NULL`` pointer "escape" to the Python user. @@ -1226,8 +929,8 @@ the module whose functions one wishes to call might not have been loaded yet! Portability therefore requires not to make any assumptions about symbol visibility. This means that all symbols in extension modules should be declared ``static``, except for the module's initialization function, in order to -avoid name clashes with other extension modules (as discussed in section -:ref:`methodtable`). And it means that symbols that *should* be accessible from +avoid name clashes with other extension modules. And it means that symbols +that *should* be accessible from other extension modules must be exported in a different way. Python provides a special mechanism to pass C-level information (pointers) from @@ -1269,8 +972,9 @@ file corresponding to the module provides a macro that takes care of importing the module and retrieving its C API pointers; client modules only have to call this macro before accessing the C API. -The exporting module is a modification of the :mod:`!spam` module from section -:ref:`extending-simpleexample`. The function :func:`!spam.system` does not call +The exporting module is a modification of the :mod:`!spam` module from the +:ref:`tutorial `. +The function :func:`!spam.system` does not call the C library function :c:func:`system` directly, but a function :c:func:`!PySpam_System`, which would of course do something more complicated in reality (such as adding "spam" to every command). This function @@ -1412,15 +1116,14 @@ code distribution). .. rubric:: Footnotes -.. [#] An interface for this function already exists in the standard module :mod:`os` - --- it was chosen as a simple and straightforward example. +.. [#borrow] The metaphor of "borrowing" a reference is not completely correct: + the owner still has a copy of the reference. -.. [#] The metaphor of "borrowing" a reference is not completely correct: the owner - still has a copy of the reference. - -.. [#] Checking that the reference count is at least 1 **does not work** --- the +.. [#dont-check-refcount] Checking that the reference count is at least 1 + **does not work** --- the reference count itself could be in freed memory and may thus be reused for another object! -.. [#] These guarantees don't hold when you use the "old" style calling convention --- +.. [#old-calling-convention] These guarantees don't hold when you use the + "old" style calling convention --- this is still found in much existing code. diff --git a/Doc/extending/first-extension-module.rst b/Doc/extending/first-extension-module.rst new file mode 100644 index 00000000000..5bde785c49e --- /dev/null +++ b/Doc/extending/first-extension-module.rst @@ -0,0 +1,667 @@ +.. highlight:: c + + +.. _extending-simpleexample: +.. _first-extension-module: + +********************************* +Your first C API extension module +********************************* + +This tutorial will take you through creating a simple +Python extension module written in C or C++. + +We will use the low-level Python C API directly. +For easier ways to create extension modules, see +the :ref:`recommended third party tools `. + +The tutorial assumes basic knowledge about Python: you should be able to +define functions in Python code before starting to write them in C. +See :ref:`tutorial-index` for an introduction to Python itself. + +The tutorial should be approachable for anyone who can write a basic C library. +While we will mention several concepts that a C beginner would not be expected +to know, like ``static`` functions or linkage declarations, understanding these +is not necessary for success. + +We will focus on giving you a "feel" of what Python's C API is like. +It will not teach you important concepts, like error handling +and reference counting, which are covered in later chapters. + +We will assume that you use a Unix-like system (including macOS and +Linux), or Windows. +On other systems, you might need to adjust some details -- for example, +a system command name. + +You need to have a suitable C compiler and Python development headers installed. +On Linux, headers are often in a package like ``python3-dev`` +or ``python3-devel``. + +You need to be able to install Python packages. +This tutorial uses `pip `__ (``pip install``), but you +can substitute any tool that can build and install ``pyproject.toml``-based +projects, like `uv `_ (``uv pip install``). +Preferably, have a :ref:`virtual environment ` activated. + + +.. note:: + + This tutorial uses APIs that were added in CPython 3.15. + To create an extension that's compatible with earlier versions of CPython, + please follow an earlier version of this documentation. + + This tutorial uses C syntax added in C11 and C++20. + If your extension needs to be compatible with earlier standards, + please follow tutorials in documentation for Python 3.14 or below. + + +What we'll do +============= + +Let's create an extension module called ``spam`` [#why-spam]_, +which will include a Python interface to the C +standard library function :c:func:`system`. +This function is defined in ``stdlib.h``. +It takes a C string as argument, runs the argument as a system +command, and returns a result value as an integer. +A manual page for :c:func:`system` might summarize it this way:: + + #include + int system(const char *command); + +Note that like many functions in the C standard library, +this function is already exposed in Python. +In production, use :py:func:`os.system` or :py:func:`subprocess.run` +rather than the module you'll write here. + +We want this function to be callable from Python as follows: + +.. code-block:: pycon + + >>> import spam + >>> status = spam.system("whoami") + User Name + >>> status + 0 + +.. note:: + + The system command ``whoami`` prints out your username. + It's useful in tutorials like this one because it has the same name on + both Unix and Windows. + + +Start with the headers +====================== + +Begin by creating a directory for this tutorial, and switching to it +on the command line. +Then, create a file named :file:`spammodule.c` in your directory. +[#why-spammodule]_ + +In this file, we'll include two headers: :file:`Python.h` to pull in +all declarations of the Python C API, and :file:`stdlib.h` for the +:c:func:`system` function. [#stdlib-h]_ + +Add the following lines to :file:`spammodule.c`: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-at: + :end-at: + +Be sure to put :file:`stdlib.h`, and any other standard library includes, +*after* :file:`Python.h`. +On some systems, Python may define some pre-processor definitions +that affect the standard headers. + + +Running your build tool +======================= + +With only the includes in place, your extension won't do anything. +Still, it's a good time to compile it and try to import it. +This will ensure that your build tool works, so that you can make +and test incremental changes as you follow the rest of the text. + +CPython itself does not come with a tool to build extension modules; +it is recommended to use a third-party project for this. +In this tutorial, we'll use `meson-python`_. +(If you want to use another one, see :ref:`first-extension-other-tools`.) + +.. at the time of writing, meson-python has the least overhead for a + simple extension using PyModExport. + Change this if another tool makes things easier. + +``meson-python`` requires defining a "project" using two extra files. + +First, add ``pyproject.toml`` with these contents: + +.. code-block:: toml + + [build-system] + build-backend = 'mesonpy' + requires = ['meson-python'] + + [project] + # Placeholder project information + # (change this before distributing the module) + name = 'sampleproject' + version = '0' + +Then, create ``meson.build`` containing the following: + +.. code-block:: meson + + project('sampleproject', 'c') + + py = import('python').find_installation(pure: false) + + py.extension_module( + 'spam', # name of the importable Python module + 'spammodule.c', # the C source file + install: true, + ) + +.. note:: + + See `meson-python documentation `_ for details on + configuration. + +Now, build install the *project in the current directory* (``.``) via ``pip``: + +.. code-block:: sh + + python -m pip install . + +.. tip:: + + If you don't have ``pip`` installed, run ``python -m ensurepip``, + preferably in a :ref:`virtual environment `. + (Or, if you prefer another tool that can build and install + ``pyproject.toml``-based projects, use that.) + +.. _meson-python: https://mesonbuild.com/meson-python/ +.. _virtual environment: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#create-and-use-virtual-environments + +Note that you will need to run this command again every time you change your +extension. +Unlike Python, C has an explicit compilation step. + +When your extension is compiled and installed, start Python and try to +import it. +This should fail with the following exception: + +.. code-block:: pycon + + >>> import spam + Traceback (most recent call last): + ... + ImportError: dynamic module does not define module export function (PyModExport_spam or PyInit_spam) + + +Module export hook +================== + +The exception you got when you tried to import the module told you that Python +is looking for a "module export function", also known as a +:ref:`module export hook `. +Let's define one. + +First, add a prototype below the ``#include`` lines: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-after: /// Export hook prototype + :end-before: /// + +.. tip:: + The prototype is not strictly necessary, but some modern compilers emit + warnings without it. + It's generally better to add the prototype than to disable the warning. + +The :c:macro:`PyMODEXPORT_FUNC` macro declares the function's +return type, and adds any special linkage declarations needed +to make the function visible and usable when CPython loads it. + +After the prototype, add the function itself. +For now, make it return ``NULL``: + +.. code-block:: c + + PyMODEXPORT_FUNC + PyModExport_spam(void) + { + return NULL; + } + +Compile and load the module again. +You should get a different error this time. + +.. code-block:: pycon + + >>> import spam + Traceback (most recent call last): + ... + SystemError: module export hook for module 'spam' failed without setting an exception + +Simply returning ``NULL`` is *not* correct behavior for an export hook, +and CPython complains about it. +That's good -- it means that CPython found the function! +Let's now make it do something useful. + + +The slot table +============== + +Rather than ``NULL``, the export hook should return the information needed to +create a module. +Let's start with the basics: the name and docstring. + +The information should be defined in a ``static`` array of +:c:type:`PyModuleDef_Slot` entries, which are essentially key-value pairs. +Define this array just before your export hook: + +.. code-block:: c + + static PyModuleDef_Slot spam_slots[] = { + {Py_mod_name, "spam"}, + {Py_mod_doc, "A wonderful module with an example function"}, + {0, NULL} + }; + +For both :c:data:`Py_mod_name` and :c:data:`Py_mod_doc`, the values are C +strings -- that is, NUL-terminated, UTF-8 encoded byte arrays. + +Note the zero-filled sentinel entry at the end. +If you forget it, you'll trigger undefined behavior. + +The array is defined as ``static`` -- that is, not visible outside this ``.c`` file. +This will be a common theme. +CPython only needs to access the export hook; all global variables +and all other functions should generally be ``static``, so that they don't +clash with other extensions. + +Return this array from your export hook instead of ``NULL``: + +.. code-block:: c + :emphasize-lines: 4 + + PyMODEXPORT_FUNC + PyModExport_spam(void) + { + return spam_slots; + } + +Now, recompile and try it out: + +.. code-block:: pycon + + >>> import spam + >>> print(spam) + + +You have an extension module! +Try ``help(spam)`` to see the docstring. + +The next step will be adding a function. + + +.. _backtoexample: + +Exposing a function +=================== + +To expose the :c:func:`system` C function directly to Python, +we'll need to write a layer of glue code to convert arguments from Python +objects to C values, and the C return value back to Python. + +One of the simplest ways to write glue code is a ":c:data:`METH_O`" function, +which takes two Python objects and returns one. +All Python objects -- regardless of the Python type -- are represented in C +as pointers to the :c:type:`PyObject` structure. + +Add such a function above the slots array:: + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + Py_RETURN_NONE; + } + +For now, we ignore the arguments, and use the :c:macro:`Py_RETURN_NONE` +macro, which expands to a ``return`` statement that properly returns +a Python :py:data:`None` object. + +Recompile your extension to make sure you don't have syntax errors. +We haven't yet added ``spam_system`` to the module, so you might get a +warning that ``spam_system`` is unused. + +.. _methodtable: + +Method definitions +------------------ + +To expose the C function to Python, you will need to provide several pieces of +information in a structure called +:c:type:`PyMethodDef` [#why-pymethoddef]_: + +* ``ml_name``: the name of the Python function; +* ``ml_doc``: a docstring; +* ``ml_meth``: the C function to be called; and +* ``ml_flags``: a set of flags describing details like how Python arguments are + passed to the C function. + We'll use :c:data:`METH_O` here -- the flag that matches our + ``spam_system`` function's signature. + +Because modules typically create several functions, these definitions +need to be collected in an array, with a zero-filled sentinel at the end. +Add this array just below the ``spam_system`` function: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-after: /// Module method table + :end-before: /// + +As with module slots, a zero-filled sentinel marks the end of the array. + +Next, we'll add the method to the module. +Add a :c:data:`Py_mod_methods` slot to your :c:type:`PyMethodDef` array: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-after: /// Module slot table + :end-before: /// + :emphasize-lines: 5 + +Recompile your extension again, and test it. +Be sure to restart the Python interpreter, so that ``import spam`` picks +up the new version of the module. + +You should now be able to call the function: + +.. code-block:: pycon + + >>> import spam + >>> print(spam.system) + + >>> print(spam.system('whoami')) + None + +Note that our ``spam.system`` does not yet run the ``whoami`` command; +it only returns ``None``. + +Check that the function accepts exactly one argument, as specified by +the :c:data:`METH_O` flag: + +.. code-block:: pycon + + >>> print(spam.system('too', 'many', 'arguments')) + Traceback (most recent call last): + ... + TypeError: spam.system() takes exactly one argument (3 given) + + +Returning an integer +==================== + +Now, let's take a look at the return value. +Instead of ``None``, we'll want ``spam.system`` to return a number -- that is, +a Python :py:type:`int` object. +Eventually this will be the exit code of a system command, +but let's start with a fixed value, say, ``3``. + +The Python C API provides a function to create a Python :py:type:`int` object +from a C ``int`` value: :c:func:`PyLong_FromLong`. [#why-pylongfromlong]_ + +To call it, replace the ``Py_RETURN_NONE`` with the following 3 lines: + +.. this could be a one-liner, but we want to show the data types here + +.. code-block:: c + :emphasize-lines: 4-6 + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + int status = 3; + PyObject *result = PyLong_FromLong(status); + return result; + } + + +Recompile, restart the Python interpreter again, +and check that the function now returns 3: + +.. code-block:: pycon + + >>> import spam + >>> spam.system('whoami') + 3 + + +Accepting a string +================== + +Finally, let's handle the function argument. + +Our C function, :c:func:`!spam_system`, takes two arguments. +The first one, ``PyObject *self``, will be set to the ``spam`` module +object. +This isn't useful in our case, so we'll ignore it. + +The other one, ``PyObject *arg``, will be set to the object that the user +passed from Python. +We expect that it should be a Python string. +In order to use the information in it, we will need +to convert it to a C value -- in this case, a C string (``const char *``). + +There's a slight type mismatch here: Python's :py:class:`str` objects store +Unicode text, but C strings are arrays of bytes. +So, we'll need to *encode* the data, and we'll use the UTF-8 encoding for it. +(UTF-8 might not always be correct for system commands, but it's what +:py:meth:`str.encode` uses by default, +and the C API has special support for it.) + +The function to encode a Python string into a UTF-8 buffer is named +:c:func:`PyUnicode_AsUTF8` [#why-pyunicodeasutf8]_. +Call it like this: + +.. code-block:: c + :emphasize-lines: 4 + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + const char *command = PyUnicode_AsUTF8(arg); + int status = 3; + PyObject *result = PyLong_FromLong(status); + return result; + } + +If :c:func:`PyUnicode_AsUTF8` is successful, *command* will point to the +resulting array of bytes. +This buffer is managed by the *arg* object, which means we don't need to free +it, but we must follow some rules: + +* We should only use the buffer inside the ``spam_system`` function. + When ``spam_system`` returns, *arg* and the buffer it manages might be + garbage-collected. +* We must not modify it. This is why we use ``const``. + +If :c:func:`PyUnicode_AsUTF8` was *not* successful, it returns a ``NULL`` +pointer. +When calling *any* Python C API, we always need to handle such error cases. +The way to do this in general is left for later chapters of this documentation. +For now, be assured that we are already handling errors from +:c:func:`PyLong_FromLong` correctly. + +For the :c:func:`PyUnicode_AsUTF8` call, the correct way to handle errors is +returning ``NULL`` from ``spam_system``. +Add an ``if`` block for this: + + +.. code-block:: c + :emphasize-lines: 5-7 + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + const char *command = PyUnicode_AsUTF8(arg); + if (command == NULL) { + return NULL; + } + int status = 3; + PyObject *result = PyLong_FromLong(status); + return result; + } + +That's it for the setup. +Now, all that is left is calling the C library function :c:func:`system` with +the ``char *`` buffer, and using its result instead of the ``3``: + +.. code-block:: c + :emphasize-lines: 8 + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + const char *command = PyUnicode_AsUTF8(arg); + if (command == NULL) { + return NULL; + } + int status = system(command); + PyObject *result = PyLong_FromLong(status); + return result; + } + +Compile your module, restart Python, and test. +This time, you should see your username -- the output of the ``whoami`` +system command: + +.. code-block:: pycon + + >>> import spam + >>> result = spam.system('whoami') + User Name + >>> result + 0 + +You might also want to test error cases: + +.. code-block:: pycon + + >>> import spam + >>> result = spam.system('nonexistent-command') + sh: line 1: nonexistent-command: command not found + >>> result + 32512 + + >>> spam.system(3) + Traceback (most recent call last): + ... + TypeError: bad argument type for built-in operation + + +The result +========== + + +Congratulations! +You have written a complete Python C API extension module, +and completed this tutorial! + +Here is the entire source file, for your convenience: + +.. _extending-spammodule-source: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-at: /// + + +.. _first-extension-other-tools: + +Appendix: Other build tools +=========================== + +You should be able to follow this tutorial -- except the +*Running your build tool* section itself -- with a build tool other +than ``meson-python``. + +The Python Packaging User Guide has a `list of recommended tools `_; +be sure to choose one for the C language. + + +Workaround for missing PyInit function +-------------------------------------- + +If your build tool output complains about missing ``PyInit_spam``, +add the following function to your module for now: + +.. code-block:: c + + // A workaround + void *PyInit_spam(void) { return NULL; } + +This is a shim for an old-style :ref:`initialization function `, +which was required in extension modules for CPython 3.14 and below. +Current CPython does not need it, but some build tools may still assume that +all extension modules need to define it. + +If you use this workaround, you will get the exception +``SystemError: initialization of spam failed without raising an exception`` +instead of +``ImportError: dynamic module does not define module export function``. + + +Compiling directly +------------------ + +Using a third-party build tool is heavily recommended, +as it will take care of various details of your platform and Python +installation, of naming the resulting extension, and, later, of distributing +your work. + +If you are building an extension for as *specific* system, or for yourself +only, you might instead want to run your compiler directly. +The way to do this is system-specific; be prepared for issues you will need +to solve yourself. + +Linux +^^^^^ + +On Linux, the Python development package may include a ``python3-config`` +command that prints out the required compiler flags. +If you use it, check that it corresponds to the CPython interpreter you'll use +to load the module. +Then, start with the following command: + +.. code-block:: sh + + gcc --shared $(python3-config --cflags --ldflags) spammodule.c -o spam.so + +This should generate a ``spam.so`` file that you need to put in a directory +on :py:attr:`sys.path`. + + +.. rubric:: Footnotes + +.. [#why-spam] ``spam`` is the favorite food of Monty Python fans... +.. [#why-spammodule] The source file name is entirely up to you, + though some tools can be picky about the ``.c`` extension. + This tutorial uses the traditional ``*module.c`` suffix. + Some people would just use :file:`spam.c` to implement a module + named ``spam``, + projects where Python isn't the primary language might use ``py_spam.c``, + and so on. +.. [#stdlib-h] Including :file:`stdlib.h` is technically not necessary, + since :file:`Python.h` includes it and + :ref:`several other standard headers ` for its own use + or for backwards compatibility. + However, it is good practice to explicitly include what you need. +.. [#why-pymethoddef] The :c:type:`!PyMethodDef` structure is also used + to create methods of classes, so there's no separate + ":c:type:`!PyFunctionDef`". +.. [#why-pylongfromlong] The name :c:func:`PyLong_FromLong` + might not seem obvious. + ``PyLong`` refers to a the Python :py:class:`int`, which was originally + called ``long``; the ``FromLong`` refers to the C ``long`` (or ``long int``) + type. +.. [#why-pyunicodeasutf8] Here, ``PyUnicode`` refers to the original name of + the Python :py:class:`str` class: ``unicode``. diff --git a/Doc/extending/index.rst b/Doc/extending/index.rst index 4cc2c96d8d5..c0c494c3059 100644 --- a/Doc/extending/index.rst +++ b/Doc/extending/index.rst @@ -5,15 +5,17 @@ ################################################## This document describes how to write modules in C or C++ to extend the Python -interpreter with new modules. Those modules can not only define new functions -but also new object types and their methods. The document also describes how +interpreter with new modules. Those modules can do what Python code does -- +define functions, object types and methods -- but also interact with native +libraries or achieve better performance by avoiding the overhead of an +interpreter. The document also describes how to embed the Python interpreter in another application, for use as an extension language. Finally, it shows how to compile and link extension modules so that they can be loaded dynamically (at run time) into the interpreter, if the underlying operating system supports this feature. -This document assumes basic knowledge about Python. For an informal -introduction to the language, see :ref:`tutorial-index`. :ref:`reference-index` +This document assumes basic knowledge about C and Python. For an informal +introduction to Python, see :ref:`tutorial-index`. :ref:`reference-index` gives a more formal definition of the language. :ref:`library-index` documents the existing object types, functions and modules (both built-in and written in Python) that give the language its wide application range. @@ -21,37 +23,75 @@ Python) that give the language its wide application range. For a detailed description of the whole Python/C API, see the separate :ref:`c-api-index`. +To support extensions, Python's C API (Application Programmers Interface) +defines a set of functions, macros and variables that provide access to most +aspects of the Python run-time system. The Python API is incorporated in a C +source file by including the header ``"Python.h"``. + +.. note:: + + The C extension interface is specific to CPython, and extension modules do + not work on other Python implementations. In many cases, it is possible to + avoid writing C extensions and preserve portability to other implementations. + For example, if your use case is calling C library functions or system calls, + you should consider using the :mod:`ctypes` module or the `cffi + `_ library rather than writing + custom C code. + These modules let you write Python code to interface with C code and are more + portable between implementations of Python than writing and compiling a C + extension module. + + +.. toctree:: + :hidden: + + first-extension-module.rst + extending.rst + newtypes_tutorial.rst + newtypes.rst + building.rst + windows.rst + embedding.rst + Recommended third party tools ============================= -This guide only covers the basic tools for creating extensions provided +This document only covers the basic tools for creating extensions provided as part of this version of CPython. Some :ref:`third party tools ` offer both simpler and more sophisticated approaches to creating C and C++ extensions for Python. +While this document is aimed at extension authors, it should also be helpful to +the authors of such tools. +For example, the tutorial module can serve as a simple test case for a build +tool or sample expected output of a code generator. -Creating extensions without third party tools -============================================= + +C API Tutorial +============== + +This tutorial describes how to write a simple module in C or C++, +using the Python C API -- that is, using the basic tools provided +as part of this version of CPython. + + +#. :ref:`first-extension-module` + + +Guides for intermediate topics +============================== This section of the guide covers creating C and C++ extensions without assistance from third party tools. It is intended primarily for creators of those tools, rather than being a recommended way to create your own C extensions. -.. seealso:: - - :pep:`489` -- Multi-phase extension module initialization - -.. toctree:: - :maxdepth: 2 - :numbered: - - extending.rst - newtypes_tutorial.rst - newtypes.rst - building.rst - windows.rst +* :ref:`extending-intro` +* :ref:`defining-new-types` +* :ref:`new-types-topics` +* :ref:`building` +* :ref:`building-on-windows` Embedding the CPython runtime in a larger application ===================================================== @@ -61,8 +101,4 @@ interpreter as the main application, it is desirable to instead embed the CPython runtime inside a larger application. This section covers some of the details involved in doing that successfully. -.. toctree:: - :maxdepth: 2 - :numbered: - - embedding.rst +* :ref:`embedding` diff --git a/Doc/includes/capi-extension/spammodule-01.c b/Doc/includes/capi-extension/spammodule-01.c new file mode 100644 index 00000000000..86c9840359d --- /dev/null +++ b/Doc/includes/capi-extension/spammodule-01.c @@ -0,0 +1,55 @@ +/* This file needs to be kept in sync with the tutorial + * at Doc/extending/first-extension-module.rst + */ + +/// Includes + +#include +#include // for system() + +/// Implementation of spam.system + +static PyObject * +spam_system(PyObject *self, PyObject *arg) +{ + const char *command = PyUnicode_AsUTF8(arg); + if (command == NULL) { + return NULL; + } + int status = system(command); + PyObject *result = PyLong_FromLong(status); + return result; +} + +/// Module method table + +static PyMethodDef spam_methods[] = { + { + .ml_name="system", + .ml_meth=spam_system, + .ml_flags=METH_O, + .ml_doc="Execute a shell command.", + }, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +/// Module slot table + +static PyModuleDef_Slot spam_slots[] = { + {Py_mod_name, "spam"}, + {Py_mod_doc, "A wonderful module with an example function"}, + {Py_mod_methods, spam_methods}, + {0, NULL} +}; + +/// Export hook prototype + +PyMODEXPORT_FUNC PyModExport_spam(void); + +/// Module export hook + +PyMODEXPORT_FUNC +PyModExport_spam(void) +{ + return spam_slots; +} From 786f464c740a2f2bf60871feaf73df87b315877b Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Sat, 20 Dec 2025 01:43:36 +0800 Subject: [PATCH 50/80] gh-142961: Fix constant folding len(tuple) in JIT (GH-142963) --- Include/internal/pycore_uop_ids.h | 1543 +++++++++-------- Include/internal/pycore_uop_metadata.h | 21 + Lib/test/test_capi/test_opt.py | 12 + ...-12-19-00-59-29.gh-issue-142961.q8WRSq.rst | 1 + Python/bytecodes.c | 7 + Python/executor_cases.c.h | 100 ++ Python/optimizer_bytecodes.c | 6 +- Python/optimizer_cases.c.h | 18 +- 8 files changed, 931 insertions(+), 777 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-19-00-59-29.gh-issue-142961.q8WRSq.rst diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index e9851b767bc..6130e5cad21 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -314,788 +314,793 @@ extern "C" { #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _SPILL_OR_RELOAD 524 -#define _START_EXECUTOR 525 -#define _STORE_ATTR 526 -#define _STORE_ATTR_INSTANCE_VALUE 527 -#define _STORE_ATTR_SLOT 528 -#define _STORE_ATTR_WITH_HINT 529 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW 524 +#define _SPILL_OR_RELOAD 525 +#define _START_EXECUTOR 526 +#define _STORE_ATTR 527 +#define _STORE_ATTR_INSTANCE_VALUE 528 +#define _STORE_ATTR_SLOT 529 +#define _STORE_ATTR_WITH_HINT 530 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 530 -#define _STORE_FAST_0 531 -#define _STORE_FAST_1 532 -#define _STORE_FAST_2 533 -#define _STORE_FAST_3 534 -#define _STORE_FAST_4 535 -#define _STORE_FAST_5 536 -#define _STORE_FAST_6 537 -#define _STORE_FAST_7 538 +#define _STORE_FAST 531 +#define _STORE_FAST_0 532 +#define _STORE_FAST_1 533 +#define _STORE_FAST_2 534 +#define _STORE_FAST_3 535 +#define _STORE_FAST_4 536 +#define _STORE_FAST_5 537 +#define _STORE_FAST_6 538 +#define _STORE_FAST_7 539 #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 539 -#define _STORE_SUBSCR 540 -#define _STORE_SUBSCR_DICT 541 -#define _STORE_SUBSCR_LIST_INT 542 -#define _SWAP 543 -#define _SWAP_2 544 -#define _SWAP_3 545 -#define _TIER2_RESUME_CHECK 546 -#define _TO_BOOL 547 +#define _STORE_SLICE 540 +#define _STORE_SUBSCR 541 +#define _STORE_SUBSCR_DICT 542 +#define _STORE_SUBSCR_LIST_INT 543 +#define _SWAP 544 +#define _SWAP_2 545 +#define _SWAP_3 546 +#define _TIER2_RESUME_CHECK 547 +#define _TO_BOOL 548 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT -#define _TO_BOOL_LIST 548 +#define _TO_BOOL_LIST 549 #define _TO_BOOL_NONE TO_BOOL_NONE -#define _TO_BOOL_STR 549 +#define _TO_BOOL_STR 550 #define _TRACE_RECORD TRACE_RECORD #define _UNARY_INVERT UNARY_INVERT #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 550 -#define _UNPACK_SEQUENCE_LIST 551 -#define _UNPACK_SEQUENCE_TUPLE 552 -#define _UNPACK_SEQUENCE_TWO_TUPLE 553 +#define _UNPACK_SEQUENCE 551 +#define _UNPACK_SEQUENCE_LIST 552 +#define _UNPACK_SEQUENCE_TUPLE 553 +#define _UNPACK_SEQUENCE_TWO_TUPLE 554 #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 553 -#define _BINARY_OP_r21 554 -#define _BINARY_OP_ADD_FLOAT_r03 555 -#define _BINARY_OP_ADD_FLOAT_r13 556 -#define _BINARY_OP_ADD_FLOAT_r23 557 -#define _BINARY_OP_ADD_INT_r03 558 -#define _BINARY_OP_ADD_INT_r13 559 -#define _BINARY_OP_ADD_INT_r23 560 -#define _BINARY_OP_ADD_UNICODE_r03 561 -#define _BINARY_OP_ADD_UNICODE_r13 562 -#define _BINARY_OP_ADD_UNICODE_r23 563 -#define _BINARY_OP_EXTEND_r21 564 -#define _BINARY_OP_INPLACE_ADD_UNICODE_r20 565 -#define _BINARY_OP_MULTIPLY_FLOAT_r03 566 -#define _BINARY_OP_MULTIPLY_FLOAT_r13 567 -#define _BINARY_OP_MULTIPLY_FLOAT_r23 568 -#define _BINARY_OP_MULTIPLY_INT_r03 569 -#define _BINARY_OP_MULTIPLY_INT_r13 570 -#define _BINARY_OP_MULTIPLY_INT_r23 571 -#define _BINARY_OP_SUBSCR_CHECK_FUNC_r23 572 -#define _BINARY_OP_SUBSCR_DICT_r21 573 -#define _BINARY_OP_SUBSCR_INIT_CALL_r01 574 -#define _BINARY_OP_SUBSCR_INIT_CALL_r11 575 -#define _BINARY_OP_SUBSCR_INIT_CALL_r21 576 -#define _BINARY_OP_SUBSCR_INIT_CALL_r31 577 -#define _BINARY_OP_SUBSCR_LIST_INT_r23 578 -#define _BINARY_OP_SUBSCR_LIST_SLICE_r21 579 -#define _BINARY_OP_SUBSCR_STR_INT_r23 580 -#define _BINARY_OP_SUBSCR_TUPLE_INT_r21 581 -#define _BINARY_OP_SUBTRACT_FLOAT_r03 582 -#define _BINARY_OP_SUBTRACT_FLOAT_r13 583 -#define _BINARY_OP_SUBTRACT_FLOAT_r23 584 -#define _BINARY_OP_SUBTRACT_INT_r03 585 -#define _BINARY_OP_SUBTRACT_INT_r13 586 -#define _BINARY_OP_SUBTRACT_INT_r23 587 -#define _BINARY_SLICE_r31 588 -#define _BUILD_INTERPOLATION_r01 589 -#define _BUILD_LIST_r01 590 -#define _BUILD_MAP_r01 591 -#define _BUILD_SET_r01 592 -#define _BUILD_SLICE_r01 593 -#define _BUILD_STRING_r01 594 -#define _BUILD_TEMPLATE_r21 595 -#define _BUILD_TUPLE_r01 596 -#define _CALL_BUILTIN_CLASS_r01 597 -#define _CALL_BUILTIN_FAST_r01 598 -#define _CALL_BUILTIN_FAST_WITH_KEYWORDS_r01 599 -#define _CALL_BUILTIN_O_r03 600 -#define _CALL_INTRINSIC_1_r11 601 -#define _CALL_INTRINSIC_2_r21 602 -#define _CALL_ISINSTANCE_r31 603 -#define _CALL_KW_NON_PY_r11 604 -#define _CALL_LEN_r33 605 -#define _CALL_LIST_APPEND_r02 606 -#define _CALL_LIST_APPEND_r12 607 -#define _CALL_LIST_APPEND_r22 608 -#define _CALL_LIST_APPEND_r32 609 -#define _CALL_METHOD_DESCRIPTOR_FAST_r01 610 -#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01 611 -#define _CALL_METHOD_DESCRIPTOR_NOARGS_r01 612 -#define _CALL_METHOD_DESCRIPTOR_O_r01 613 -#define _CALL_NON_PY_GENERAL_r01 614 -#define _CALL_STR_1_r32 615 -#define _CALL_TUPLE_1_r32 616 -#define _CALL_TYPE_1_r31 617 -#define _CHECK_AND_ALLOCATE_OBJECT_r00 618 -#define _CHECK_ATTR_CLASS_r01 619 -#define _CHECK_ATTR_CLASS_r11 620 -#define _CHECK_ATTR_CLASS_r22 621 -#define _CHECK_ATTR_CLASS_r33 622 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r01 623 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r11 624 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r22 625 -#define _CHECK_ATTR_METHOD_LAZY_DICT_r33 626 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS_r00 627 -#define _CHECK_EG_MATCH_r22 628 -#define _CHECK_EXC_MATCH_r22 629 -#define _CHECK_FUNCTION_EXACT_ARGS_r00 630 -#define _CHECK_FUNCTION_VERSION_r00 631 -#define _CHECK_FUNCTION_VERSION_INLINE_r00 632 -#define _CHECK_FUNCTION_VERSION_INLINE_r11 633 -#define _CHECK_FUNCTION_VERSION_INLINE_r22 634 -#define _CHECK_FUNCTION_VERSION_INLINE_r33 635 -#define _CHECK_FUNCTION_VERSION_KW_r11 636 -#define _CHECK_IS_NOT_PY_CALLABLE_r00 637 -#define _CHECK_IS_NOT_PY_CALLABLE_KW_r11 638 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r01 639 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r11 640 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r22 641 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r33 642 -#define _CHECK_METHOD_VERSION_r00 643 -#define _CHECK_METHOD_VERSION_KW_r11 644 -#define _CHECK_PEP_523_r00 645 -#define _CHECK_PEP_523_r11 646 -#define _CHECK_PEP_523_r22 647 -#define _CHECK_PEP_523_r33 648 -#define _CHECK_PERIODIC_r00 649 -#define _CHECK_PERIODIC_AT_END_r00 650 -#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM_r00 651 -#define _CHECK_RECURSION_REMAINING_r00 652 -#define _CHECK_RECURSION_REMAINING_r11 653 -#define _CHECK_RECURSION_REMAINING_r22 654 -#define _CHECK_RECURSION_REMAINING_r33 655 -#define _CHECK_STACK_SPACE_r00 656 -#define _CHECK_STACK_SPACE_OPERAND_r00 657 -#define _CHECK_STACK_SPACE_OPERAND_r11 658 -#define _CHECK_STACK_SPACE_OPERAND_r22 659 -#define _CHECK_STACK_SPACE_OPERAND_r33 660 -#define _CHECK_VALIDITY_r00 661 -#define _CHECK_VALIDITY_r11 662 -#define _CHECK_VALIDITY_r22 663 -#define _CHECK_VALIDITY_r33 664 -#define _COLD_DYNAMIC_EXIT_r00 665 -#define _COLD_EXIT_r00 666 -#define _COMPARE_OP_r21 667 -#define _COMPARE_OP_FLOAT_r01 668 -#define _COMPARE_OP_FLOAT_r11 669 -#define _COMPARE_OP_FLOAT_r21 670 -#define _COMPARE_OP_FLOAT_r32 671 -#define _COMPARE_OP_INT_r23 672 -#define _COMPARE_OP_STR_r21 673 -#define _CONTAINS_OP_r21 674 -#define _CONTAINS_OP_DICT_r21 675 -#define _CONTAINS_OP_SET_r21 676 -#define _CONVERT_VALUE_r11 677 -#define _COPY_r01 678 -#define _COPY_1_r02 679 -#define _COPY_1_r12 680 -#define _COPY_1_r23 681 -#define _COPY_2_r03 682 -#define _COPY_2_r13 683 -#define _COPY_2_r23 684 -#define _COPY_3_r03 685 -#define _COPY_3_r13 686 -#define _COPY_3_r23 687 -#define _COPY_3_r33 688 -#define _COPY_FREE_VARS_r00 689 -#define _COPY_FREE_VARS_r11 690 -#define _COPY_FREE_VARS_r22 691 -#define _COPY_FREE_VARS_r33 692 -#define _CREATE_INIT_FRAME_r01 693 -#define _DELETE_ATTR_r10 694 -#define _DELETE_DEREF_r00 695 -#define _DELETE_FAST_r00 696 -#define _DELETE_GLOBAL_r00 697 -#define _DELETE_NAME_r00 698 -#define _DELETE_SUBSCR_r20 699 -#define _DEOPT_r00 700 -#define _DEOPT_r10 701 -#define _DEOPT_r20 702 -#define _DEOPT_r30 703 -#define _DICT_MERGE_r10 704 -#define _DICT_UPDATE_r10 705 -#define _DO_CALL_r01 706 -#define _DO_CALL_FUNCTION_EX_r31 707 -#define _DO_CALL_KW_r11 708 -#define _DYNAMIC_EXIT_r00 709 -#define _DYNAMIC_EXIT_r10 710 -#define _DYNAMIC_EXIT_r20 711 -#define _DYNAMIC_EXIT_r30 712 -#define _END_FOR_r10 713 -#define _END_SEND_r21 714 -#define _ERROR_POP_N_r00 715 -#define _EXIT_INIT_CHECK_r10 716 -#define _EXIT_TRACE_r00 717 -#define _EXIT_TRACE_r10 718 -#define _EXIT_TRACE_r20 719 -#define _EXIT_TRACE_r30 720 -#define _EXPAND_METHOD_r00 721 -#define _EXPAND_METHOD_KW_r11 722 -#define _FATAL_ERROR_r00 723 -#define _FATAL_ERROR_r11 724 -#define _FATAL_ERROR_r22 725 -#define _FATAL_ERROR_r33 726 -#define _FORMAT_SIMPLE_r11 727 -#define _FORMAT_WITH_SPEC_r21 728 -#define _FOR_ITER_r23 729 -#define _FOR_ITER_GEN_FRAME_r23 730 -#define _FOR_ITER_TIER_TWO_r23 731 -#define _GET_AITER_r11 732 -#define _GET_ANEXT_r12 733 -#define _GET_AWAITABLE_r11 734 -#define _GET_ITER_r12 735 -#define _GET_LEN_r12 736 -#define _GET_YIELD_FROM_ITER_r11 737 -#define _GUARD_BINARY_OP_EXTEND_r22 738 -#define _GUARD_CALLABLE_ISINSTANCE_r03 739 -#define _GUARD_CALLABLE_ISINSTANCE_r13 740 -#define _GUARD_CALLABLE_ISINSTANCE_r23 741 -#define _GUARD_CALLABLE_ISINSTANCE_r33 742 -#define _GUARD_CALLABLE_LEN_r03 743 -#define _GUARD_CALLABLE_LEN_r13 744 -#define _GUARD_CALLABLE_LEN_r23 745 -#define _GUARD_CALLABLE_LEN_r33 746 -#define _GUARD_CALLABLE_LIST_APPEND_r03 747 -#define _GUARD_CALLABLE_LIST_APPEND_r13 748 -#define _GUARD_CALLABLE_LIST_APPEND_r23 749 -#define _GUARD_CALLABLE_LIST_APPEND_r33 750 -#define _GUARD_CALLABLE_STR_1_r03 751 -#define _GUARD_CALLABLE_STR_1_r13 752 -#define _GUARD_CALLABLE_STR_1_r23 753 -#define _GUARD_CALLABLE_STR_1_r33 754 -#define _GUARD_CALLABLE_TUPLE_1_r03 755 -#define _GUARD_CALLABLE_TUPLE_1_r13 756 -#define _GUARD_CALLABLE_TUPLE_1_r23 757 -#define _GUARD_CALLABLE_TUPLE_1_r33 758 -#define _GUARD_CALLABLE_TYPE_1_r03 759 -#define _GUARD_CALLABLE_TYPE_1_r13 760 -#define _GUARD_CALLABLE_TYPE_1_r23 761 -#define _GUARD_CALLABLE_TYPE_1_r33 762 -#define _GUARD_DORV_NO_DICT_r01 763 -#define _GUARD_DORV_NO_DICT_r11 764 -#define _GUARD_DORV_NO_DICT_r22 765 -#define _GUARD_DORV_NO_DICT_r33 766 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r01 767 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11 768 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22 769 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33 770 -#define _GUARD_GLOBALS_VERSION_r00 771 -#define _GUARD_GLOBALS_VERSION_r11 772 -#define _GUARD_GLOBALS_VERSION_r22 773 -#define _GUARD_GLOBALS_VERSION_r33 774 -#define _GUARD_IP_RETURN_GENERATOR_r00 775 -#define _GUARD_IP_RETURN_GENERATOR_r11 776 -#define _GUARD_IP_RETURN_GENERATOR_r22 777 -#define _GUARD_IP_RETURN_GENERATOR_r33 778 -#define _GUARD_IP_RETURN_VALUE_r00 779 -#define _GUARD_IP_RETURN_VALUE_r11 780 -#define _GUARD_IP_RETURN_VALUE_r22 781 -#define _GUARD_IP_RETURN_VALUE_r33 782 -#define _GUARD_IP_YIELD_VALUE_r00 783 -#define _GUARD_IP_YIELD_VALUE_r11 784 -#define _GUARD_IP_YIELD_VALUE_r22 785 -#define _GUARD_IP_YIELD_VALUE_r33 786 -#define _GUARD_IP__PUSH_FRAME_r00 787 -#define _GUARD_IP__PUSH_FRAME_r11 788 -#define _GUARD_IP__PUSH_FRAME_r22 789 -#define _GUARD_IP__PUSH_FRAME_r33 790 -#define _GUARD_IS_FALSE_POP_r00 791 -#define _GUARD_IS_FALSE_POP_r10 792 -#define _GUARD_IS_FALSE_POP_r21 793 -#define _GUARD_IS_FALSE_POP_r32 794 -#define _GUARD_IS_NONE_POP_r00 795 -#define _GUARD_IS_NONE_POP_r10 796 -#define _GUARD_IS_NONE_POP_r21 797 -#define _GUARD_IS_NONE_POP_r32 798 -#define _GUARD_IS_NOT_NONE_POP_r10 799 -#define _GUARD_IS_TRUE_POP_r00 800 -#define _GUARD_IS_TRUE_POP_r10 801 -#define _GUARD_IS_TRUE_POP_r21 802 -#define _GUARD_IS_TRUE_POP_r32 803 -#define _GUARD_KEYS_VERSION_r01 804 -#define _GUARD_KEYS_VERSION_r11 805 -#define _GUARD_KEYS_VERSION_r22 806 -#define _GUARD_KEYS_VERSION_r33 807 -#define _GUARD_NOS_DICT_r02 808 -#define _GUARD_NOS_DICT_r12 809 -#define _GUARD_NOS_DICT_r22 810 -#define _GUARD_NOS_DICT_r33 811 -#define _GUARD_NOS_FLOAT_r02 812 -#define _GUARD_NOS_FLOAT_r12 813 -#define _GUARD_NOS_FLOAT_r22 814 -#define _GUARD_NOS_FLOAT_r33 815 -#define _GUARD_NOS_INT_r02 816 -#define _GUARD_NOS_INT_r12 817 -#define _GUARD_NOS_INT_r22 818 -#define _GUARD_NOS_INT_r33 819 -#define _GUARD_NOS_LIST_r02 820 -#define _GUARD_NOS_LIST_r12 821 -#define _GUARD_NOS_LIST_r22 822 -#define _GUARD_NOS_LIST_r33 823 -#define _GUARD_NOS_NOT_NULL_r02 824 -#define _GUARD_NOS_NOT_NULL_r12 825 -#define _GUARD_NOS_NOT_NULL_r22 826 -#define _GUARD_NOS_NOT_NULL_r33 827 -#define _GUARD_NOS_NULL_r02 828 -#define _GUARD_NOS_NULL_r12 829 -#define _GUARD_NOS_NULL_r22 830 -#define _GUARD_NOS_NULL_r33 831 -#define _GUARD_NOS_OVERFLOWED_r02 832 -#define _GUARD_NOS_OVERFLOWED_r12 833 -#define _GUARD_NOS_OVERFLOWED_r22 834 -#define _GUARD_NOS_OVERFLOWED_r33 835 -#define _GUARD_NOS_TUPLE_r02 836 -#define _GUARD_NOS_TUPLE_r12 837 -#define _GUARD_NOS_TUPLE_r22 838 -#define _GUARD_NOS_TUPLE_r33 839 -#define _GUARD_NOS_UNICODE_r02 840 -#define _GUARD_NOS_UNICODE_r12 841 -#define _GUARD_NOS_UNICODE_r22 842 -#define _GUARD_NOS_UNICODE_r33 843 -#define _GUARD_NOT_EXHAUSTED_LIST_r02 844 -#define _GUARD_NOT_EXHAUSTED_LIST_r12 845 -#define _GUARD_NOT_EXHAUSTED_LIST_r22 846 -#define _GUARD_NOT_EXHAUSTED_LIST_r33 847 -#define _GUARD_NOT_EXHAUSTED_RANGE_r02 848 -#define _GUARD_NOT_EXHAUSTED_RANGE_r12 849 -#define _GUARD_NOT_EXHAUSTED_RANGE_r22 850 -#define _GUARD_NOT_EXHAUSTED_RANGE_r33 851 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r02 852 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r12 853 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r22 854 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r33 855 -#define _GUARD_THIRD_NULL_r03 856 -#define _GUARD_THIRD_NULL_r13 857 -#define _GUARD_THIRD_NULL_r23 858 -#define _GUARD_THIRD_NULL_r33 859 -#define _GUARD_TOS_ANY_SET_r01 860 -#define _GUARD_TOS_ANY_SET_r11 861 -#define _GUARD_TOS_ANY_SET_r22 862 -#define _GUARD_TOS_ANY_SET_r33 863 -#define _GUARD_TOS_DICT_r01 864 -#define _GUARD_TOS_DICT_r11 865 -#define _GUARD_TOS_DICT_r22 866 -#define _GUARD_TOS_DICT_r33 867 -#define _GUARD_TOS_FLOAT_r01 868 -#define _GUARD_TOS_FLOAT_r11 869 -#define _GUARD_TOS_FLOAT_r22 870 -#define _GUARD_TOS_FLOAT_r33 871 -#define _GUARD_TOS_INT_r01 872 -#define _GUARD_TOS_INT_r11 873 -#define _GUARD_TOS_INT_r22 874 -#define _GUARD_TOS_INT_r33 875 -#define _GUARD_TOS_LIST_r01 876 -#define _GUARD_TOS_LIST_r11 877 -#define _GUARD_TOS_LIST_r22 878 -#define _GUARD_TOS_LIST_r33 879 -#define _GUARD_TOS_OVERFLOWED_r01 880 -#define _GUARD_TOS_OVERFLOWED_r11 881 -#define _GUARD_TOS_OVERFLOWED_r22 882 -#define _GUARD_TOS_OVERFLOWED_r33 883 -#define _GUARD_TOS_SLICE_r01 884 -#define _GUARD_TOS_SLICE_r11 885 -#define _GUARD_TOS_SLICE_r22 886 -#define _GUARD_TOS_SLICE_r33 887 -#define _GUARD_TOS_TUPLE_r01 888 -#define _GUARD_TOS_TUPLE_r11 889 -#define _GUARD_TOS_TUPLE_r22 890 -#define _GUARD_TOS_TUPLE_r33 891 -#define _GUARD_TOS_UNICODE_r01 892 -#define _GUARD_TOS_UNICODE_r11 893 -#define _GUARD_TOS_UNICODE_r22 894 -#define _GUARD_TOS_UNICODE_r33 895 -#define _GUARD_TYPE_VERSION_r01 896 -#define _GUARD_TYPE_VERSION_r11 897 -#define _GUARD_TYPE_VERSION_r22 898 -#define _GUARD_TYPE_VERSION_r33 899 -#define _GUARD_TYPE_VERSION_AND_LOCK_r01 900 -#define _GUARD_TYPE_VERSION_AND_LOCK_r11 901 -#define _GUARD_TYPE_VERSION_AND_LOCK_r22 902 -#define _GUARD_TYPE_VERSION_AND_LOCK_r33 903 -#define _HANDLE_PENDING_AND_DEOPT_r00 904 -#define _HANDLE_PENDING_AND_DEOPT_r10 905 -#define _HANDLE_PENDING_AND_DEOPT_r20 906 -#define _HANDLE_PENDING_AND_DEOPT_r30 907 -#define _IMPORT_FROM_r12 908 -#define _IMPORT_NAME_r21 909 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS_r00 910 -#define _INIT_CALL_PY_EXACT_ARGS_r01 911 -#define _INIT_CALL_PY_EXACT_ARGS_0_r01 912 -#define _INIT_CALL_PY_EXACT_ARGS_1_r01 913 -#define _INIT_CALL_PY_EXACT_ARGS_2_r01 914 -#define _INIT_CALL_PY_EXACT_ARGS_3_r01 915 -#define _INIT_CALL_PY_EXACT_ARGS_4_r01 916 -#define _INSERT_NULL_r10 917 -#define _INSTRUMENTED_FOR_ITER_r23 918 -#define _INSTRUMENTED_INSTRUCTION_r00 919 -#define _INSTRUMENTED_JUMP_FORWARD_r00 920 -#define _INSTRUMENTED_JUMP_FORWARD_r11 921 -#define _INSTRUMENTED_JUMP_FORWARD_r22 922 -#define _INSTRUMENTED_JUMP_FORWARD_r33 923 -#define _INSTRUMENTED_LINE_r00 924 -#define _INSTRUMENTED_NOT_TAKEN_r00 925 -#define _INSTRUMENTED_NOT_TAKEN_r11 926 -#define _INSTRUMENTED_NOT_TAKEN_r22 927 -#define _INSTRUMENTED_NOT_TAKEN_r33 928 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r00 929 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r10 930 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r21 931 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r32 932 -#define _INSTRUMENTED_POP_JUMP_IF_NONE_r10 933 -#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE_r10 934 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r00 935 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r10 936 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r21 937 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r32 938 -#define _IS_NONE_r11 939 -#define _IS_OP_r21 940 -#define _ITER_CHECK_LIST_r02 941 -#define _ITER_CHECK_LIST_r12 942 -#define _ITER_CHECK_LIST_r22 943 -#define _ITER_CHECK_LIST_r33 944 -#define _ITER_CHECK_RANGE_r02 945 -#define _ITER_CHECK_RANGE_r12 946 -#define _ITER_CHECK_RANGE_r22 947 -#define _ITER_CHECK_RANGE_r33 948 -#define _ITER_CHECK_TUPLE_r02 949 -#define _ITER_CHECK_TUPLE_r12 950 -#define _ITER_CHECK_TUPLE_r22 951 -#define _ITER_CHECK_TUPLE_r33 952 -#define _ITER_JUMP_LIST_r02 953 -#define _ITER_JUMP_LIST_r12 954 -#define _ITER_JUMP_LIST_r22 955 -#define _ITER_JUMP_LIST_r33 956 -#define _ITER_JUMP_RANGE_r02 957 -#define _ITER_JUMP_RANGE_r12 958 -#define _ITER_JUMP_RANGE_r22 959 -#define _ITER_JUMP_RANGE_r33 960 -#define _ITER_JUMP_TUPLE_r02 961 -#define _ITER_JUMP_TUPLE_r12 962 -#define _ITER_JUMP_TUPLE_r22 963 -#define _ITER_JUMP_TUPLE_r33 964 -#define _ITER_NEXT_LIST_r23 965 -#define _ITER_NEXT_LIST_TIER_TWO_r23 966 -#define _ITER_NEXT_RANGE_r03 967 -#define _ITER_NEXT_RANGE_r13 968 -#define _ITER_NEXT_RANGE_r23 969 -#define _ITER_NEXT_TUPLE_r03 970 -#define _ITER_NEXT_TUPLE_r13 971 -#define _ITER_NEXT_TUPLE_r23 972 -#define _JUMP_BACKWARD_NO_INTERRUPT_r00 973 -#define _JUMP_BACKWARD_NO_INTERRUPT_r11 974 -#define _JUMP_BACKWARD_NO_INTERRUPT_r22 975 -#define _JUMP_BACKWARD_NO_INTERRUPT_r33 976 -#define _JUMP_TO_TOP_r00 977 -#define _LIST_APPEND_r10 978 -#define _LIST_EXTEND_r10 979 -#define _LOAD_ATTR_r10 980 -#define _LOAD_ATTR_CLASS_r11 981 -#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_r11 982 -#define _LOAD_ATTR_INSTANCE_VALUE_r02 983 -#define _LOAD_ATTR_INSTANCE_VALUE_r12 984 -#define _LOAD_ATTR_INSTANCE_VALUE_r23 985 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 986 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 987 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 988 -#define _LOAD_ATTR_METHOD_NO_DICT_r02 989 -#define _LOAD_ATTR_METHOD_NO_DICT_r12 990 -#define _LOAD_ATTR_METHOD_NO_DICT_r23 991 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 992 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 993 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 994 -#define _LOAD_ATTR_MODULE_r11 995 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 996 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 997 -#define _LOAD_ATTR_PROPERTY_FRAME_r11 998 -#define _LOAD_ATTR_SLOT_r11 999 -#define _LOAD_ATTR_WITH_HINT_r11 1000 -#define _LOAD_BUILD_CLASS_r01 1001 -#define _LOAD_BYTECODE_r00 1002 -#define _LOAD_COMMON_CONSTANT_r01 1003 -#define _LOAD_COMMON_CONSTANT_r12 1004 -#define _LOAD_COMMON_CONSTANT_r23 1005 -#define _LOAD_CONST_r01 1006 -#define _LOAD_CONST_r12 1007 -#define _LOAD_CONST_r23 1008 -#define _LOAD_CONST_INLINE_r01 1009 -#define _LOAD_CONST_INLINE_r12 1010 -#define _LOAD_CONST_INLINE_r23 1011 -#define _LOAD_CONST_INLINE_BORROW_r01 1012 -#define _LOAD_CONST_INLINE_BORROW_r12 1013 -#define _LOAD_CONST_INLINE_BORROW_r23 1014 -#define _LOAD_CONST_UNDER_INLINE_r02 1015 -#define _LOAD_CONST_UNDER_INLINE_r12 1016 -#define _LOAD_CONST_UNDER_INLINE_r23 1017 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1018 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1019 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1020 -#define _LOAD_DEREF_r01 1021 -#define _LOAD_FAST_r01 1022 -#define _LOAD_FAST_r12 1023 -#define _LOAD_FAST_r23 1024 -#define _LOAD_FAST_0_r01 1025 -#define _LOAD_FAST_0_r12 1026 -#define _LOAD_FAST_0_r23 1027 -#define _LOAD_FAST_1_r01 1028 -#define _LOAD_FAST_1_r12 1029 -#define _LOAD_FAST_1_r23 1030 -#define _LOAD_FAST_2_r01 1031 -#define _LOAD_FAST_2_r12 1032 -#define _LOAD_FAST_2_r23 1033 -#define _LOAD_FAST_3_r01 1034 -#define _LOAD_FAST_3_r12 1035 -#define _LOAD_FAST_3_r23 1036 -#define _LOAD_FAST_4_r01 1037 -#define _LOAD_FAST_4_r12 1038 -#define _LOAD_FAST_4_r23 1039 -#define _LOAD_FAST_5_r01 1040 -#define _LOAD_FAST_5_r12 1041 -#define _LOAD_FAST_5_r23 1042 -#define _LOAD_FAST_6_r01 1043 -#define _LOAD_FAST_6_r12 1044 -#define _LOAD_FAST_6_r23 1045 -#define _LOAD_FAST_7_r01 1046 -#define _LOAD_FAST_7_r12 1047 -#define _LOAD_FAST_7_r23 1048 -#define _LOAD_FAST_AND_CLEAR_r01 1049 -#define _LOAD_FAST_AND_CLEAR_r12 1050 -#define _LOAD_FAST_AND_CLEAR_r23 1051 -#define _LOAD_FAST_BORROW_r01 1052 -#define _LOAD_FAST_BORROW_r12 1053 -#define _LOAD_FAST_BORROW_r23 1054 -#define _LOAD_FAST_BORROW_0_r01 1055 -#define _LOAD_FAST_BORROW_0_r12 1056 -#define _LOAD_FAST_BORROW_0_r23 1057 -#define _LOAD_FAST_BORROW_1_r01 1058 -#define _LOAD_FAST_BORROW_1_r12 1059 -#define _LOAD_FAST_BORROW_1_r23 1060 -#define _LOAD_FAST_BORROW_2_r01 1061 -#define _LOAD_FAST_BORROW_2_r12 1062 -#define _LOAD_FAST_BORROW_2_r23 1063 -#define _LOAD_FAST_BORROW_3_r01 1064 -#define _LOAD_FAST_BORROW_3_r12 1065 -#define _LOAD_FAST_BORROW_3_r23 1066 -#define _LOAD_FAST_BORROW_4_r01 1067 -#define _LOAD_FAST_BORROW_4_r12 1068 -#define _LOAD_FAST_BORROW_4_r23 1069 -#define _LOAD_FAST_BORROW_5_r01 1070 -#define _LOAD_FAST_BORROW_5_r12 1071 -#define _LOAD_FAST_BORROW_5_r23 1072 -#define _LOAD_FAST_BORROW_6_r01 1073 -#define _LOAD_FAST_BORROW_6_r12 1074 -#define _LOAD_FAST_BORROW_6_r23 1075 -#define _LOAD_FAST_BORROW_7_r01 1076 -#define _LOAD_FAST_BORROW_7_r12 1077 -#define _LOAD_FAST_BORROW_7_r23 1078 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1079 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1080 -#define _LOAD_FAST_CHECK_r01 1081 -#define _LOAD_FAST_CHECK_r12 1082 -#define _LOAD_FAST_CHECK_r23 1083 -#define _LOAD_FAST_LOAD_FAST_r02 1084 -#define _LOAD_FAST_LOAD_FAST_r13 1085 -#define _LOAD_FROM_DICT_OR_DEREF_r11 1086 -#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1087 -#define _LOAD_GLOBAL_r00 1088 -#define _LOAD_GLOBAL_BUILTINS_r01 1089 -#define _LOAD_GLOBAL_MODULE_r01 1090 -#define _LOAD_LOCALS_r01 1091 -#define _LOAD_LOCALS_r12 1092 -#define _LOAD_LOCALS_r23 1093 -#define _LOAD_NAME_r01 1094 -#define _LOAD_SMALL_INT_r01 1095 -#define _LOAD_SMALL_INT_r12 1096 -#define _LOAD_SMALL_INT_r23 1097 -#define _LOAD_SMALL_INT_0_r01 1098 -#define _LOAD_SMALL_INT_0_r12 1099 -#define _LOAD_SMALL_INT_0_r23 1100 -#define _LOAD_SMALL_INT_1_r01 1101 -#define _LOAD_SMALL_INT_1_r12 1102 -#define _LOAD_SMALL_INT_1_r23 1103 -#define _LOAD_SMALL_INT_2_r01 1104 -#define _LOAD_SMALL_INT_2_r12 1105 -#define _LOAD_SMALL_INT_2_r23 1106 -#define _LOAD_SMALL_INT_3_r01 1107 -#define _LOAD_SMALL_INT_3_r12 1108 -#define _LOAD_SMALL_INT_3_r23 1109 -#define _LOAD_SPECIAL_r00 1110 -#define _LOAD_SUPER_ATTR_ATTR_r31 1111 -#define _LOAD_SUPER_ATTR_METHOD_r32 1112 -#define _MAKE_CALLARGS_A_TUPLE_r33 1113 -#define _MAKE_CELL_r00 1114 -#define _MAKE_FUNCTION_r11 1115 -#define _MAKE_WARM_r00 1116 -#define _MAKE_WARM_r11 1117 -#define _MAKE_WARM_r22 1118 -#define _MAKE_WARM_r33 1119 -#define _MAP_ADD_r20 1120 -#define _MATCH_CLASS_r31 1121 -#define _MATCH_KEYS_r23 1122 -#define _MATCH_MAPPING_r02 1123 -#define _MATCH_MAPPING_r12 1124 -#define _MATCH_MAPPING_r23 1125 -#define _MATCH_SEQUENCE_r02 1126 -#define _MATCH_SEQUENCE_r12 1127 -#define _MATCH_SEQUENCE_r23 1128 -#define _MAYBE_EXPAND_METHOD_r00 1129 -#define _MAYBE_EXPAND_METHOD_KW_r11 1130 -#define _MONITOR_CALL_r00 1131 -#define _MONITOR_CALL_KW_r11 1132 -#define _MONITOR_JUMP_BACKWARD_r00 1133 -#define _MONITOR_JUMP_BACKWARD_r11 1134 -#define _MONITOR_JUMP_BACKWARD_r22 1135 -#define _MONITOR_JUMP_BACKWARD_r33 1136 -#define _MONITOR_RESUME_r00 1137 -#define _NOP_r00 1138 -#define _NOP_r11 1139 -#define _NOP_r22 1140 -#define _NOP_r33 1141 -#define _POP_CALL_r20 1142 -#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1143 -#define _POP_CALL_ONE_r30 1144 -#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1145 -#define _POP_CALL_TWO_r30 1146 -#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1147 -#define _POP_EXCEPT_r10 1148 -#define _POP_ITER_r20 1149 -#define _POP_JUMP_IF_FALSE_r00 1150 -#define _POP_JUMP_IF_FALSE_r10 1151 -#define _POP_JUMP_IF_FALSE_r21 1152 -#define _POP_JUMP_IF_FALSE_r32 1153 -#define _POP_JUMP_IF_TRUE_r00 1154 -#define _POP_JUMP_IF_TRUE_r10 1155 -#define _POP_JUMP_IF_TRUE_r21 1156 -#define _POP_JUMP_IF_TRUE_r32 1157 -#define _POP_TOP_r10 1158 -#define _POP_TOP_FLOAT_r00 1159 -#define _POP_TOP_FLOAT_r10 1160 -#define _POP_TOP_FLOAT_r21 1161 -#define _POP_TOP_FLOAT_r32 1162 -#define _POP_TOP_INT_r00 1163 -#define _POP_TOP_INT_r10 1164 -#define _POP_TOP_INT_r21 1165 -#define _POP_TOP_INT_r32 1166 -#define _POP_TOP_LOAD_CONST_INLINE_r11 1167 -#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1168 -#define _POP_TOP_NOP_r00 1169 -#define _POP_TOP_NOP_r10 1170 -#define _POP_TOP_NOP_r21 1171 -#define _POP_TOP_NOP_r32 1172 -#define _POP_TOP_UNICODE_r00 1173 -#define _POP_TOP_UNICODE_r10 1174 -#define _POP_TOP_UNICODE_r21 1175 -#define _POP_TOP_UNICODE_r32 1176 -#define _POP_TWO_r20 1177 -#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1178 -#define _PUSH_EXC_INFO_r02 1179 -#define _PUSH_EXC_INFO_r12 1180 -#define _PUSH_EXC_INFO_r23 1181 -#define _PUSH_FRAME_r10 1182 -#define _PUSH_NULL_r01 1183 -#define _PUSH_NULL_r12 1184 -#define _PUSH_NULL_r23 1185 -#define _PUSH_NULL_CONDITIONAL_r00 1186 -#define _PY_FRAME_GENERAL_r01 1187 -#define _PY_FRAME_KW_r11 1188 -#define _QUICKEN_RESUME_r00 1189 -#define _QUICKEN_RESUME_r11 1190 -#define _QUICKEN_RESUME_r22 1191 -#define _QUICKEN_RESUME_r33 1192 -#define _REPLACE_WITH_TRUE_r11 1193 -#define _RESUME_CHECK_r00 1194 -#define _RESUME_CHECK_r11 1195 -#define _RESUME_CHECK_r22 1196 -#define _RESUME_CHECK_r33 1197 -#define _RETURN_GENERATOR_r01 1198 -#define _RETURN_VALUE_r11 1199 -#define _SAVE_RETURN_OFFSET_r00 1200 -#define _SAVE_RETURN_OFFSET_r11 1201 -#define _SAVE_RETURN_OFFSET_r22 1202 -#define _SAVE_RETURN_OFFSET_r33 1203 -#define _SEND_r22 1204 -#define _SEND_GEN_FRAME_r22 1205 -#define _SETUP_ANNOTATIONS_r00 1206 -#define _SET_ADD_r10 1207 -#define _SET_FUNCTION_ATTRIBUTE_r01 1208 -#define _SET_FUNCTION_ATTRIBUTE_r11 1209 -#define _SET_FUNCTION_ATTRIBUTE_r21 1210 -#define _SET_FUNCTION_ATTRIBUTE_r32 1211 -#define _SET_IP_r00 1212 -#define _SET_IP_r11 1213 -#define _SET_IP_r22 1214 -#define _SET_IP_r33 1215 -#define _SET_UPDATE_r10 1216 -#define _SPILL_OR_RELOAD_r01 1217 -#define _SPILL_OR_RELOAD_r02 1218 -#define _SPILL_OR_RELOAD_r03 1219 -#define _SPILL_OR_RELOAD_r10 1220 -#define _SPILL_OR_RELOAD_r12 1221 -#define _SPILL_OR_RELOAD_r13 1222 -#define _SPILL_OR_RELOAD_r20 1223 -#define _SPILL_OR_RELOAD_r21 1224 -#define _SPILL_OR_RELOAD_r23 1225 -#define _SPILL_OR_RELOAD_r30 1226 -#define _SPILL_OR_RELOAD_r31 1227 -#define _SPILL_OR_RELOAD_r32 1228 -#define _START_EXECUTOR_r00 1229 -#define _STORE_ATTR_r20 1230 -#define _STORE_ATTR_INSTANCE_VALUE_r21 1231 -#define _STORE_ATTR_SLOT_r21 1232 -#define _STORE_ATTR_WITH_HINT_r21 1233 -#define _STORE_DEREF_r10 1234 -#define _STORE_FAST_r10 1235 -#define _STORE_FAST_0_r10 1236 -#define _STORE_FAST_1_r10 1237 -#define _STORE_FAST_2_r10 1238 -#define _STORE_FAST_3_r10 1239 -#define _STORE_FAST_4_r10 1240 -#define _STORE_FAST_5_r10 1241 -#define _STORE_FAST_6_r10 1242 -#define _STORE_FAST_7_r10 1243 -#define _STORE_FAST_LOAD_FAST_r11 1244 -#define _STORE_FAST_STORE_FAST_r20 1245 -#define _STORE_GLOBAL_r10 1246 -#define _STORE_NAME_r10 1247 -#define _STORE_SLICE_r30 1248 -#define _STORE_SUBSCR_r30 1249 -#define _STORE_SUBSCR_DICT_r31 1250 -#define _STORE_SUBSCR_LIST_INT_r32 1251 -#define _SWAP_r11 1252 -#define _SWAP_2_r02 1253 -#define _SWAP_2_r12 1254 -#define _SWAP_2_r22 1255 -#define _SWAP_2_r33 1256 -#define _SWAP_3_r03 1257 -#define _SWAP_3_r13 1258 -#define _SWAP_3_r23 1259 -#define _SWAP_3_r33 1260 -#define _TIER2_RESUME_CHECK_r00 1261 -#define _TIER2_RESUME_CHECK_r11 1262 -#define _TIER2_RESUME_CHECK_r22 1263 -#define _TIER2_RESUME_CHECK_r33 1264 -#define _TO_BOOL_r11 1265 -#define _TO_BOOL_BOOL_r01 1266 -#define _TO_BOOL_BOOL_r11 1267 -#define _TO_BOOL_BOOL_r22 1268 -#define _TO_BOOL_BOOL_r33 1269 -#define _TO_BOOL_INT_r11 1270 -#define _TO_BOOL_LIST_r11 1271 -#define _TO_BOOL_NONE_r01 1272 -#define _TO_BOOL_NONE_r11 1273 -#define _TO_BOOL_NONE_r22 1274 -#define _TO_BOOL_NONE_r33 1275 -#define _TO_BOOL_STR_r11 1276 -#define _TRACE_RECORD_r00 1277 -#define _UNARY_INVERT_r11 1278 -#define _UNARY_NEGATIVE_r11 1279 -#define _UNARY_NOT_r01 1280 -#define _UNARY_NOT_r11 1281 -#define _UNARY_NOT_r22 1282 -#define _UNARY_NOT_r33 1283 -#define _UNPACK_EX_r10 1284 -#define _UNPACK_SEQUENCE_r10 1285 -#define _UNPACK_SEQUENCE_LIST_r10 1286 -#define _UNPACK_SEQUENCE_TUPLE_r10 1287 -#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1288 -#define _WITH_EXCEPT_START_r33 1289 -#define _YIELD_VALUE_r11 1290 -#define MAX_UOP_REGS_ID 1290 +#define MAX_UOP_ID 554 +#define _BINARY_OP_r21 555 +#define _BINARY_OP_ADD_FLOAT_r03 556 +#define _BINARY_OP_ADD_FLOAT_r13 557 +#define _BINARY_OP_ADD_FLOAT_r23 558 +#define _BINARY_OP_ADD_INT_r03 559 +#define _BINARY_OP_ADD_INT_r13 560 +#define _BINARY_OP_ADD_INT_r23 561 +#define _BINARY_OP_ADD_UNICODE_r03 562 +#define _BINARY_OP_ADD_UNICODE_r13 563 +#define _BINARY_OP_ADD_UNICODE_r23 564 +#define _BINARY_OP_EXTEND_r21 565 +#define _BINARY_OP_INPLACE_ADD_UNICODE_r20 566 +#define _BINARY_OP_MULTIPLY_FLOAT_r03 567 +#define _BINARY_OP_MULTIPLY_FLOAT_r13 568 +#define _BINARY_OP_MULTIPLY_FLOAT_r23 569 +#define _BINARY_OP_MULTIPLY_INT_r03 570 +#define _BINARY_OP_MULTIPLY_INT_r13 571 +#define _BINARY_OP_MULTIPLY_INT_r23 572 +#define _BINARY_OP_SUBSCR_CHECK_FUNC_r23 573 +#define _BINARY_OP_SUBSCR_DICT_r21 574 +#define _BINARY_OP_SUBSCR_INIT_CALL_r01 575 +#define _BINARY_OP_SUBSCR_INIT_CALL_r11 576 +#define _BINARY_OP_SUBSCR_INIT_CALL_r21 577 +#define _BINARY_OP_SUBSCR_INIT_CALL_r31 578 +#define _BINARY_OP_SUBSCR_LIST_INT_r23 579 +#define _BINARY_OP_SUBSCR_LIST_SLICE_r21 580 +#define _BINARY_OP_SUBSCR_STR_INT_r23 581 +#define _BINARY_OP_SUBSCR_TUPLE_INT_r21 582 +#define _BINARY_OP_SUBTRACT_FLOAT_r03 583 +#define _BINARY_OP_SUBTRACT_FLOAT_r13 584 +#define _BINARY_OP_SUBTRACT_FLOAT_r23 585 +#define _BINARY_OP_SUBTRACT_INT_r03 586 +#define _BINARY_OP_SUBTRACT_INT_r13 587 +#define _BINARY_OP_SUBTRACT_INT_r23 588 +#define _BINARY_SLICE_r31 589 +#define _BUILD_INTERPOLATION_r01 590 +#define _BUILD_LIST_r01 591 +#define _BUILD_MAP_r01 592 +#define _BUILD_SET_r01 593 +#define _BUILD_SLICE_r01 594 +#define _BUILD_STRING_r01 595 +#define _BUILD_TEMPLATE_r21 596 +#define _BUILD_TUPLE_r01 597 +#define _CALL_BUILTIN_CLASS_r01 598 +#define _CALL_BUILTIN_FAST_r01 599 +#define _CALL_BUILTIN_FAST_WITH_KEYWORDS_r01 600 +#define _CALL_BUILTIN_O_r03 601 +#define _CALL_INTRINSIC_1_r11 602 +#define _CALL_INTRINSIC_2_r21 603 +#define _CALL_ISINSTANCE_r31 604 +#define _CALL_KW_NON_PY_r11 605 +#define _CALL_LEN_r33 606 +#define _CALL_LIST_APPEND_r02 607 +#define _CALL_LIST_APPEND_r12 608 +#define _CALL_LIST_APPEND_r22 609 +#define _CALL_LIST_APPEND_r32 610 +#define _CALL_METHOD_DESCRIPTOR_FAST_r01 611 +#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01 612 +#define _CALL_METHOD_DESCRIPTOR_NOARGS_r01 613 +#define _CALL_METHOD_DESCRIPTOR_O_r01 614 +#define _CALL_NON_PY_GENERAL_r01 615 +#define _CALL_STR_1_r32 616 +#define _CALL_TUPLE_1_r32 617 +#define _CALL_TYPE_1_r31 618 +#define _CHECK_AND_ALLOCATE_OBJECT_r00 619 +#define _CHECK_ATTR_CLASS_r01 620 +#define _CHECK_ATTR_CLASS_r11 621 +#define _CHECK_ATTR_CLASS_r22 622 +#define _CHECK_ATTR_CLASS_r33 623 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r01 624 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r11 625 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r22 626 +#define _CHECK_ATTR_METHOD_LAZY_DICT_r33 627 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS_r00 628 +#define _CHECK_EG_MATCH_r22 629 +#define _CHECK_EXC_MATCH_r22 630 +#define _CHECK_FUNCTION_EXACT_ARGS_r00 631 +#define _CHECK_FUNCTION_VERSION_r00 632 +#define _CHECK_FUNCTION_VERSION_INLINE_r00 633 +#define _CHECK_FUNCTION_VERSION_INLINE_r11 634 +#define _CHECK_FUNCTION_VERSION_INLINE_r22 635 +#define _CHECK_FUNCTION_VERSION_INLINE_r33 636 +#define _CHECK_FUNCTION_VERSION_KW_r11 637 +#define _CHECK_IS_NOT_PY_CALLABLE_r00 638 +#define _CHECK_IS_NOT_PY_CALLABLE_KW_r11 639 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r01 640 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r11 641 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r22 642 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES_r33 643 +#define _CHECK_METHOD_VERSION_r00 644 +#define _CHECK_METHOD_VERSION_KW_r11 645 +#define _CHECK_PEP_523_r00 646 +#define _CHECK_PEP_523_r11 647 +#define _CHECK_PEP_523_r22 648 +#define _CHECK_PEP_523_r33 649 +#define _CHECK_PERIODIC_r00 650 +#define _CHECK_PERIODIC_AT_END_r00 651 +#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM_r00 652 +#define _CHECK_RECURSION_REMAINING_r00 653 +#define _CHECK_RECURSION_REMAINING_r11 654 +#define _CHECK_RECURSION_REMAINING_r22 655 +#define _CHECK_RECURSION_REMAINING_r33 656 +#define _CHECK_STACK_SPACE_r00 657 +#define _CHECK_STACK_SPACE_OPERAND_r00 658 +#define _CHECK_STACK_SPACE_OPERAND_r11 659 +#define _CHECK_STACK_SPACE_OPERAND_r22 660 +#define _CHECK_STACK_SPACE_OPERAND_r33 661 +#define _CHECK_VALIDITY_r00 662 +#define _CHECK_VALIDITY_r11 663 +#define _CHECK_VALIDITY_r22 664 +#define _CHECK_VALIDITY_r33 665 +#define _COLD_DYNAMIC_EXIT_r00 666 +#define _COLD_EXIT_r00 667 +#define _COMPARE_OP_r21 668 +#define _COMPARE_OP_FLOAT_r01 669 +#define _COMPARE_OP_FLOAT_r11 670 +#define _COMPARE_OP_FLOAT_r21 671 +#define _COMPARE_OP_FLOAT_r32 672 +#define _COMPARE_OP_INT_r23 673 +#define _COMPARE_OP_STR_r21 674 +#define _CONTAINS_OP_r21 675 +#define _CONTAINS_OP_DICT_r21 676 +#define _CONTAINS_OP_SET_r21 677 +#define _CONVERT_VALUE_r11 678 +#define _COPY_r01 679 +#define _COPY_1_r02 680 +#define _COPY_1_r12 681 +#define _COPY_1_r23 682 +#define _COPY_2_r03 683 +#define _COPY_2_r13 684 +#define _COPY_2_r23 685 +#define _COPY_3_r03 686 +#define _COPY_3_r13 687 +#define _COPY_3_r23 688 +#define _COPY_3_r33 689 +#define _COPY_FREE_VARS_r00 690 +#define _COPY_FREE_VARS_r11 691 +#define _COPY_FREE_VARS_r22 692 +#define _COPY_FREE_VARS_r33 693 +#define _CREATE_INIT_FRAME_r01 694 +#define _DELETE_ATTR_r10 695 +#define _DELETE_DEREF_r00 696 +#define _DELETE_FAST_r00 697 +#define _DELETE_GLOBAL_r00 698 +#define _DELETE_NAME_r00 699 +#define _DELETE_SUBSCR_r20 700 +#define _DEOPT_r00 701 +#define _DEOPT_r10 702 +#define _DEOPT_r20 703 +#define _DEOPT_r30 704 +#define _DICT_MERGE_r10 705 +#define _DICT_UPDATE_r10 706 +#define _DO_CALL_r01 707 +#define _DO_CALL_FUNCTION_EX_r31 708 +#define _DO_CALL_KW_r11 709 +#define _DYNAMIC_EXIT_r00 710 +#define _DYNAMIC_EXIT_r10 711 +#define _DYNAMIC_EXIT_r20 712 +#define _DYNAMIC_EXIT_r30 713 +#define _END_FOR_r10 714 +#define _END_SEND_r21 715 +#define _ERROR_POP_N_r00 716 +#define _EXIT_INIT_CHECK_r10 717 +#define _EXIT_TRACE_r00 718 +#define _EXIT_TRACE_r10 719 +#define _EXIT_TRACE_r20 720 +#define _EXIT_TRACE_r30 721 +#define _EXPAND_METHOD_r00 722 +#define _EXPAND_METHOD_KW_r11 723 +#define _FATAL_ERROR_r00 724 +#define _FATAL_ERROR_r11 725 +#define _FATAL_ERROR_r22 726 +#define _FATAL_ERROR_r33 727 +#define _FORMAT_SIMPLE_r11 728 +#define _FORMAT_WITH_SPEC_r21 729 +#define _FOR_ITER_r23 730 +#define _FOR_ITER_GEN_FRAME_r23 731 +#define _FOR_ITER_TIER_TWO_r23 732 +#define _GET_AITER_r11 733 +#define _GET_ANEXT_r12 734 +#define _GET_AWAITABLE_r11 735 +#define _GET_ITER_r12 736 +#define _GET_LEN_r12 737 +#define _GET_YIELD_FROM_ITER_r11 738 +#define _GUARD_BINARY_OP_EXTEND_r22 739 +#define _GUARD_CALLABLE_ISINSTANCE_r03 740 +#define _GUARD_CALLABLE_ISINSTANCE_r13 741 +#define _GUARD_CALLABLE_ISINSTANCE_r23 742 +#define _GUARD_CALLABLE_ISINSTANCE_r33 743 +#define _GUARD_CALLABLE_LEN_r03 744 +#define _GUARD_CALLABLE_LEN_r13 745 +#define _GUARD_CALLABLE_LEN_r23 746 +#define _GUARD_CALLABLE_LEN_r33 747 +#define _GUARD_CALLABLE_LIST_APPEND_r03 748 +#define _GUARD_CALLABLE_LIST_APPEND_r13 749 +#define _GUARD_CALLABLE_LIST_APPEND_r23 750 +#define _GUARD_CALLABLE_LIST_APPEND_r33 751 +#define _GUARD_CALLABLE_STR_1_r03 752 +#define _GUARD_CALLABLE_STR_1_r13 753 +#define _GUARD_CALLABLE_STR_1_r23 754 +#define _GUARD_CALLABLE_STR_1_r33 755 +#define _GUARD_CALLABLE_TUPLE_1_r03 756 +#define _GUARD_CALLABLE_TUPLE_1_r13 757 +#define _GUARD_CALLABLE_TUPLE_1_r23 758 +#define _GUARD_CALLABLE_TUPLE_1_r33 759 +#define _GUARD_CALLABLE_TYPE_1_r03 760 +#define _GUARD_CALLABLE_TYPE_1_r13 761 +#define _GUARD_CALLABLE_TYPE_1_r23 762 +#define _GUARD_CALLABLE_TYPE_1_r33 763 +#define _GUARD_DORV_NO_DICT_r01 764 +#define _GUARD_DORV_NO_DICT_r11 765 +#define _GUARD_DORV_NO_DICT_r22 766 +#define _GUARD_DORV_NO_DICT_r33 767 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r01 768 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11 769 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22 770 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33 771 +#define _GUARD_GLOBALS_VERSION_r00 772 +#define _GUARD_GLOBALS_VERSION_r11 773 +#define _GUARD_GLOBALS_VERSION_r22 774 +#define _GUARD_GLOBALS_VERSION_r33 775 +#define _GUARD_IP_RETURN_GENERATOR_r00 776 +#define _GUARD_IP_RETURN_GENERATOR_r11 777 +#define _GUARD_IP_RETURN_GENERATOR_r22 778 +#define _GUARD_IP_RETURN_GENERATOR_r33 779 +#define _GUARD_IP_RETURN_VALUE_r00 780 +#define _GUARD_IP_RETURN_VALUE_r11 781 +#define _GUARD_IP_RETURN_VALUE_r22 782 +#define _GUARD_IP_RETURN_VALUE_r33 783 +#define _GUARD_IP_YIELD_VALUE_r00 784 +#define _GUARD_IP_YIELD_VALUE_r11 785 +#define _GUARD_IP_YIELD_VALUE_r22 786 +#define _GUARD_IP_YIELD_VALUE_r33 787 +#define _GUARD_IP__PUSH_FRAME_r00 788 +#define _GUARD_IP__PUSH_FRAME_r11 789 +#define _GUARD_IP__PUSH_FRAME_r22 790 +#define _GUARD_IP__PUSH_FRAME_r33 791 +#define _GUARD_IS_FALSE_POP_r00 792 +#define _GUARD_IS_FALSE_POP_r10 793 +#define _GUARD_IS_FALSE_POP_r21 794 +#define _GUARD_IS_FALSE_POP_r32 795 +#define _GUARD_IS_NONE_POP_r00 796 +#define _GUARD_IS_NONE_POP_r10 797 +#define _GUARD_IS_NONE_POP_r21 798 +#define _GUARD_IS_NONE_POP_r32 799 +#define _GUARD_IS_NOT_NONE_POP_r10 800 +#define _GUARD_IS_TRUE_POP_r00 801 +#define _GUARD_IS_TRUE_POP_r10 802 +#define _GUARD_IS_TRUE_POP_r21 803 +#define _GUARD_IS_TRUE_POP_r32 804 +#define _GUARD_KEYS_VERSION_r01 805 +#define _GUARD_KEYS_VERSION_r11 806 +#define _GUARD_KEYS_VERSION_r22 807 +#define _GUARD_KEYS_VERSION_r33 808 +#define _GUARD_NOS_DICT_r02 809 +#define _GUARD_NOS_DICT_r12 810 +#define _GUARD_NOS_DICT_r22 811 +#define _GUARD_NOS_DICT_r33 812 +#define _GUARD_NOS_FLOAT_r02 813 +#define _GUARD_NOS_FLOAT_r12 814 +#define _GUARD_NOS_FLOAT_r22 815 +#define _GUARD_NOS_FLOAT_r33 816 +#define _GUARD_NOS_INT_r02 817 +#define _GUARD_NOS_INT_r12 818 +#define _GUARD_NOS_INT_r22 819 +#define _GUARD_NOS_INT_r33 820 +#define _GUARD_NOS_LIST_r02 821 +#define _GUARD_NOS_LIST_r12 822 +#define _GUARD_NOS_LIST_r22 823 +#define _GUARD_NOS_LIST_r33 824 +#define _GUARD_NOS_NOT_NULL_r02 825 +#define _GUARD_NOS_NOT_NULL_r12 826 +#define _GUARD_NOS_NOT_NULL_r22 827 +#define _GUARD_NOS_NOT_NULL_r33 828 +#define _GUARD_NOS_NULL_r02 829 +#define _GUARD_NOS_NULL_r12 830 +#define _GUARD_NOS_NULL_r22 831 +#define _GUARD_NOS_NULL_r33 832 +#define _GUARD_NOS_OVERFLOWED_r02 833 +#define _GUARD_NOS_OVERFLOWED_r12 834 +#define _GUARD_NOS_OVERFLOWED_r22 835 +#define _GUARD_NOS_OVERFLOWED_r33 836 +#define _GUARD_NOS_TUPLE_r02 837 +#define _GUARD_NOS_TUPLE_r12 838 +#define _GUARD_NOS_TUPLE_r22 839 +#define _GUARD_NOS_TUPLE_r33 840 +#define _GUARD_NOS_UNICODE_r02 841 +#define _GUARD_NOS_UNICODE_r12 842 +#define _GUARD_NOS_UNICODE_r22 843 +#define _GUARD_NOS_UNICODE_r33 844 +#define _GUARD_NOT_EXHAUSTED_LIST_r02 845 +#define _GUARD_NOT_EXHAUSTED_LIST_r12 846 +#define _GUARD_NOT_EXHAUSTED_LIST_r22 847 +#define _GUARD_NOT_EXHAUSTED_LIST_r33 848 +#define _GUARD_NOT_EXHAUSTED_RANGE_r02 849 +#define _GUARD_NOT_EXHAUSTED_RANGE_r12 850 +#define _GUARD_NOT_EXHAUSTED_RANGE_r22 851 +#define _GUARD_NOT_EXHAUSTED_RANGE_r33 852 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r02 853 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r12 854 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r22 855 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r33 856 +#define _GUARD_THIRD_NULL_r03 857 +#define _GUARD_THIRD_NULL_r13 858 +#define _GUARD_THIRD_NULL_r23 859 +#define _GUARD_THIRD_NULL_r33 860 +#define _GUARD_TOS_ANY_SET_r01 861 +#define _GUARD_TOS_ANY_SET_r11 862 +#define _GUARD_TOS_ANY_SET_r22 863 +#define _GUARD_TOS_ANY_SET_r33 864 +#define _GUARD_TOS_DICT_r01 865 +#define _GUARD_TOS_DICT_r11 866 +#define _GUARD_TOS_DICT_r22 867 +#define _GUARD_TOS_DICT_r33 868 +#define _GUARD_TOS_FLOAT_r01 869 +#define _GUARD_TOS_FLOAT_r11 870 +#define _GUARD_TOS_FLOAT_r22 871 +#define _GUARD_TOS_FLOAT_r33 872 +#define _GUARD_TOS_INT_r01 873 +#define _GUARD_TOS_INT_r11 874 +#define _GUARD_TOS_INT_r22 875 +#define _GUARD_TOS_INT_r33 876 +#define _GUARD_TOS_LIST_r01 877 +#define _GUARD_TOS_LIST_r11 878 +#define _GUARD_TOS_LIST_r22 879 +#define _GUARD_TOS_LIST_r33 880 +#define _GUARD_TOS_OVERFLOWED_r01 881 +#define _GUARD_TOS_OVERFLOWED_r11 882 +#define _GUARD_TOS_OVERFLOWED_r22 883 +#define _GUARD_TOS_OVERFLOWED_r33 884 +#define _GUARD_TOS_SLICE_r01 885 +#define _GUARD_TOS_SLICE_r11 886 +#define _GUARD_TOS_SLICE_r22 887 +#define _GUARD_TOS_SLICE_r33 888 +#define _GUARD_TOS_TUPLE_r01 889 +#define _GUARD_TOS_TUPLE_r11 890 +#define _GUARD_TOS_TUPLE_r22 891 +#define _GUARD_TOS_TUPLE_r33 892 +#define _GUARD_TOS_UNICODE_r01 893 +#define _GUARD_TOS_UNICODE_r11 894 +#define _GUARD_TOS_UNICODE_r22 895 +#define _GUARD_TOS_UNICODE_r33 896 +#define _GUARD_TYPE_VERSION_r01 897 +#define _GUARD_TYPE_VERSION_r11 898 +#define _GUARD_TYPE_VERSION_r22 899 +#define _GUARD_TYPE_VERSION_r33 900 +#define _GUARD_TYPE_VERSION_AND_LOCK_r01 901 +#define _GUARD_TYPE_VERSION_AND_LOCK_r11 902 +#define _GUARD_TYPE_VERSION_AND_LOCK_r22 903 +#define _GUARD_TYPE_VERSION_AND_LOCK_r33 904 +#define _HANDLE_PENDING_AND_DEOPT_r00 905 +#define _HANDLE_PENDING_AND_DEOPT_r10 906 +#define _HANDLE_PENDING_AND_DEOPT_r20 907 +#define _HANDLE_PENDING_AND_DEOPT_r30 908 +#define _IMPORT_FROM_r12 909 +#define _IMPORT_NAME_r21 910 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS_r00 911 +#define _INIT_CALL_PY_EXACT_ARGS_r01 912 +#define _INIT_CALL_PY_EXACT_ARGS_0_r01 913 +#define _INIT_CALL_PY_EXACT_ARGS_1_r01 914 +#define _INIT_CALL_PY_EXACT_ARGS_2_r01 915 +#define _INIT_CALL_PY_EXACT_ARGS_3_r01 916 +#define _INIT_CALL_PY_EXACT_ARGS_4_r01 917 +#define _INSERT_NULL_r10 918 +#define _INSTRUMENTED_FOR_ITER_r23 919 +#define _INSTRUMENTED_INSTRUCTION_r00 920 +#define _INSTRUMENTED_JUMP_FORWARD_r00 921 +#define _INSTRUMENTED_JUMP_FORWARD_r11 922 +#define _INSTRUMENTED_JUMP_FORWARD_r22 923 +#define _INSTRUMENTED_JUMP_FORWARD_r33 924 +#define _INSTRUMENTED_LINE_r00 925 +#define _INSTRUMENTED_NOT_TAKEN_r00 926 +#define _INSTRUMENTED_NOT_TAKEN_r11 927 +#define _INSTRUMENTED_NOT_TAKEN_r22 928 +#define _INSTRUMENTED_NOT_TAKEN_r33 929 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r00 930 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r10 931 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r21 932 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r32 933 +#define _INSTRUMENTED_POP_JUMP_IF_NONE_r10 934 +#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE_r10 935 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r00 936 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r10 937 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r21 938 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r32 939 +#define _IS_NONE_r11 940 +#define _IS_OP_r21 941 +#define _ITER_CHECK_LIST_r02 942 +#define _ITER_CHECK_LIST_r12 943 +#define _ITER_CHECK_LIST_r22 944 +#define _ITER_CHECK_LIST_r33 945 +#define _ITER_CHECK_RANGE_r02 946 +#define _ITER_CHECK_RANGE_r12 947 +#define _ITER_CHECK_RANGE_r22 948 +#define _ITER_CHECK_RANGE_r33 949 +#define _ITER_CHECK_TUPLE_r02 950 +#define _ITER_CHECK_TUPLE_r12 951 +#define _ITER_CHECK_TUPLE_r22 952 +#define _ITER_CHECK_TUPLE_r33 953 +#define _ITER_JUMP_LIST_r02 954 +#define _ITER_JUMP_LIST_r12 955 +#define _ITER_JUMP_LIST_r22 956 +#define _ITER_JUMP_LIST_r33 957 +#define _ITER_JUMP_RANGE_r02 958 +#define _ITER_JUMP_RANGE_r12 959 +#define _ITER_JUMP_RANGE_r22 960 +#define _ITER_JUMP_RANGE_r33 961 +#define _ITER_JUMP_TUPLE_r02 962 +#define _ITER_JUMP_TUPLE_r12 963 +#define _ITER_JUMP_TUPLE_r22 964 +#define _ITER_JUMP_TUPLE_r33 965 +#define _ITER_NEXT_LIST_r23 966 +#define _ITER_NEXT_LIST_TIER_TWO_r23 967 +#define _ITER_NEXT_RANGE_r03 968 +#define _ITER_NEXT_RANGE_r13 969 +#define _ITER_NEXT_RANGE_r23 970 +#define _ITER_NEXT_TUPLE_r03 971 +#define _ITER_NEXT_TUPLE_r13 972 +#define _ITER_NEXT_TUPLE_r23 973 +#define _JUMP_BACKWARD_NO_INTERRUPT_r00 974 +#define _JUMP_BACKWARD_NO_INTERRUPT_r11 975 +#define _JUMP_BACKWARD_NO_INTERRUPT_r22 976 +#define _JUMP_BACKWARD_NO_INTERRUPT_r33 977 +#define _JUMP_TO_TOP_r00 978 +#define _LIST_APPEND_r10 979 +#define _LIST_EXTEND_r10 980 +#define _LOAD_ATTR_r10 981 +#define _LOAD_ATTR_CLASS_r11 982 +#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_r11 983 +#define _LOAD_ATTR_INSTANCE_VALUE_r02 984 +#define _LOAD_ATTR_INSTANCE_VALUE_r12 985 +#define _LOAD_ATTR_INSTANCE_VALUE_r23 986 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 987 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 988 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 989 +#define _LOAD_ATTR_METHOD_NO_DICT_r02 990 +#define _LOAD_ATTR_METHOD_NO_DICT_r12 991 +#define _LOAD_ATTR_METHOD_NO_DICT_r23 992 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 993 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 994 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 995 +#define _LOAD_ATTR_MODULE_r11 996 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 997 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 998 +#define _LOAD_ATTR_PROPERTY_FRAME_r11 999 +#define _LOAD_ATTR_SLOT_r11 1000 +#define _LOAD_ATTR_WITH_HINT_r11 1001 +#define _LOAD_BUILD_CLASS_r01 1002 +#define _LOAD_BYTECODE_r00 1003 +#define _LOAD_COMMON_CONSTANT_r01 1004 +#define _LOAD_COMMON_CONSTANT_r12 1005 +#define _LOAD_COMMON_CONSTANT_r23 1006 +#define _LOAD_CONST_r01 1007 +#define _LOAD_CONST_r12 1008 +#define _LOAD_CONST_r23 1009 +#define _LOAD_CONST_INLINE_r01 1010 +#define _LOAD_CONST_INLINE_r12 1011 +#define _LOAD_CONST_INLINE_r23 1012 +#define _LOAD_CONST_INLINE_BORROW_r01 1013 +#define _LOAD_CONST_INLINE_BORROW_r12 1014 +#define _LOAD_CONST_INLINE_BORROW_r23 1015 +#define _LOAD_CONST_UNDER_INLINE_r02 1016 +#define _LOAD_CONST_UNDER_INLINE_r12 1017 +#define _LOAD_CONST_UNDER_INLINE_r23 1018 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1019 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1020 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1021 +#define _LOAD_DEREF_r01 1022 +#define _LOAD_FAST_r01 1023 +#define _LOAD_FAST_r12 1024 +#define _LOAD_FAST_r23 1025 +#define _LOAD_FAST_0_r01 1026 +#define _LOAD_FAST_0_r12 1027 +#define _LOAD_FAST_0_r23 1028 +#define _LOAD_FAST_1_r01 1029 +#define _LOAD_FAST_1_r12 1030 +#define _LOAD_FAST_1_r23 1031 +#define _LOAD_FAST_2_r01 1032 +#define _LOAD_FAST_2_r12 1033 +#define _LOAD_FAST_2_r23 1034 +#define _LOAD_FAST_3_r01 1035 +#define _LOAD_FAST_3_r12 1036 +#define _LOAD_FAST_3_r23 1037 +#define _LOAD_FAST_4_r01 1038 +#define _LOAD_FAST_4_r12 1039 +#define _LOAD_FAST_4_r23 1040 +#define _LOAD_FAST_5_r01 1041 +#define _LOAD_FAST_5_r12 1042 +#define _LOAD_FAST_5_r23 1043 +#define _LOAD_FAST_6_r01 1044 +#define _LOAD_FAST_6_r12 1045 +#define _LOAD_FAST_6_r23 1046 +#define _LOAD_FAST_7_r01 1047 +#define _LOAD_FAST_7_r12 1048 +#define _LOAD_FAST_7_r23 1049 +#define _LOAD_FAST_AND_CLEAR_r01 1050 +#define _LOAD_FAST_AND_CLEAR_r12 1051 +#define _LOAD_FAST_AND_CLEAR_r23 1052 +#define _LOAD_FAST_BORROW_r01 1053 +#define _LOAD_FAST_BORROW_r12 1054 +#define _LOAD_FAST_BORROW_r23 1055 +#define _LOAD_FAST_BORROW_0_r01 1056 +#define _LOAD_FAST_BORROW_0_r12 1057 +#define _LOAD_FAST_BORROW_0_r23 1058 +#define _LOAD_FAST_BORROW_1_r01 1059 +#define _LOAD_FAST_BORROW_1_r12 1060 +#define _LOAD_FAST_BORROW_1_r23 1061 +#define _LOAD_FAST_BORROW_2_r01 1062 +#define _LOAD_FAST_BORROW_2_r12 1063 +#define _LOAD_FAST_BORROW_2_r23 1064 +#define _LOAD_FAST_BORROW_3_r01 1065 +#define _LOAD_FAST_BORROW_3_r12 1066 +#define _LOAD_FAST_BORROW_3_r23 1067 +#define _LOAD_FAST_BORROW_4_r01 1068 +#define _LOAD_FAST_BORROW_4_r12 1069 +#define _LOAD_FAST_BORROW_4_r23 1070 +#define _LOAD_FAST_BORROW_5_r01 1071 +#define _LOAD_FAST_BORROW_5_r12 1072 +#define _LOAD_FAST_BORROW_5_r23 1073 +#define _LOAD_FAST_BORROW_6_r01 1074 +#define _LOAD_FAST_BORROW_6_r12 1075 +#define _LOAD_FAST_BORROW_6_r23 1076 +#define _LOAD_FAST_BORROW_7_r01 1077 +#define _LOAD_FAST_BORROW_7_r12 1078 +#define _LOAD_FAST_BORROW_7_r23 1079 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1080 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1081 +#define _LOAD_FAST_CHECK_r01 1082 +#define _LOAD_FAST_CHECK_r12 1083 +#define _LOAD_FAST_CHECK_r23 1084 +#define _LOAD_FAST_LOAD_FAST_r02 1085 +#define _LOAD_FAST_LOAD_FAST_r13 1086 +#define _LOAD_FROM_DICT_OR_DEREF_r11 1087 +#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1088 +#define _LOAD_GLOBAL_r00 1089 +#define _LOAD_GLOBAL_BUILTINS_r01 1090 +#define _LOAD_GLOBAL_MODULE_r01 1091 +#define _LOAD_LOCALS_r01 1092 +#define _LOAD_LOCALS_r12 1093 +#define _LOAD_LOCALS_r23 1094 +#define _LOAD_NAME_r01 1095 +#define _LOAD_SMALL_INT_r01 1096 +#define _LOAD_SMALL_INT_r12 1097 +#define _LOAD_SMALL_INT_r23 1098 +#define _LOAD_SMALL_INT_0_r01 1099 +#define _LOAD_SMALL_INT_0_r12 1100 +#define _LOAD_SMALL_INT_0_r23 1101 +#define _LOAD_SMALL_INT_1_r01 1102 +#define _LOAD_SMALL_INT_1_r12 1103 +#define _LOAD_SMALL_INT_1_r23 1104 +#define _LOAD_SMALL_INT_2_r01 1105 +#define _LOAD_SMALL_INT_2_r12 1106 +#define _LOAD_SMALL_INT_2_r23 1107 +#define _LOAD_SMALL_INT_3_r01 1108 +#define _LOAD_SMALL_INT_3_r12 1109 +#define _LOAD_SMALL_INT_3_r23 1110 +#define _LOAD_SPECIAL_r00 1111 +#define _LOAD_SUPER_ATTR_ATTR_r31 1112 +#define _LOAD_SUPER_ATTR_METHOD_r32 1113 +#define _MAKE_CALLARGS_A_TUPLE_r33 1114 +#define _MAKE_CELL_r00 1115 +#define _MAKE_FUNCTION_r11 1116 +#define _MAKE_WARM_r00 1117 +#define _MAKE_WARM_r11 1118 +#define _MAKE_WARM_r22 1119 +#define _MAKE_WARM_r33 1120 +#define _MAP_ADD_r20 1121 +#define _MATCH_CLASS_r31 1122 +#define _MATCH_KEYS_r23 1123 +#define _MATCH_MAPPING_r02 1124 +#define _MATCH_MAPPING_r12 1125 +#define _MATCH_MAPPING_r23 1126 +#define _MATCH_SEQUENCE_r02 1127 +#define _MATCH_SEQUENCE_r12 1128 +#define _MATCH_SEQUENCE_r23 1129 +#define _MAYBE_EXPAND_METHOD_r00 1130 +#define _MAYBE_EXPAND_METHOD_KW_r11 1131 +#define _MONITOR_CALL_r00 1132 +#define _MONITOR_CALL_KW_r11 1133 +#define _MONITOR_JUMP_BACKWARD_r00 1134 +#define _MONITOR_JUMP_BACKWARD_r11 1135 +#define _MONITOR_JUMP_BACKWARD_r22 1136 +#define _MONITOR_JUMP_BACKWARD_r33 1137 +#define _MONITOR_RESUME_r00 1138 +#define _NOP_r00 1139 +#define _NOP_r11 1140 +#define _NOP_r22 1141 +#define _NOP_r33 1142 +#define _POP_CALL_r20 1143 +#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1144 +#define _POP_CALL_ONE_r30 1145 +#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1146 +#define _POP_CALL_TWO_r30 1147 +#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1148 +#define _POP_EXCEPT_r10 1149 +#define _POP_ITER_r20 1150 +#define _POP_JUMP_IF_FALSE_r00 1151 +#define _POP_JUMP_IF_FALSE_r10 1152 +#define _POP_JUMP_IF_FALSE_r21 1153 +#define _POP_JUMP_IF_FALSE_r32 1154 +#define _POP_JUMP_IF_TRUE_r00 1155 +#define _POP_JUMP_IF_TRUE_r10 1156 +#define _POP_JUMP_IF_TRUE_r21 1157 +#define _POP_JUMP_IF_TRUE_r32 1158 +#define _POP_TOP_r10 1159 +#define _POP_TOP_FLOAT_r00 1160 +#define _POP_TOP_FLOAT_r10 1161 +#define _POP_TOP_FLOAT_r21 1162 +#define _POP_TOP_FLOAT_r32 1163 +#define _POP_TOP_INT_r00 1164 +#define _POP_TOP_INT_r10 1165 +#define _POP_TOP_INT_r21 1166 +#define _POP_TOP_INT_r32 1167 +#define _POP_TOP_LOAD_CONST_INLINE_r11 1168 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1169 +#define _POP_TOP_NOP_r00 1170 +#define _POP_TOP_NOP_r10 1171 +#define _POP_TOP_NOP_r21 1172 +#define _POP_TOP_NOP_r32 1173 +#define _POP_TOP_UNICODE_r00 1174 +#define _POP_TOP_UNICODE_r10 1175 +#define _POP_TOP_UNICODE_r21 1176 +#define _POP_TOP_UNICODE_r32 1177 +#define _POP_TWO_r20 1178 +#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1179 +#define _PUSH_EXC_INFO_r02 1180 +#define _PUSH_EXC_INFO_r12 1181 +#define _PUSH_EXC_INFO_r23 1182 +#define _PUSH_FRAME_r10 1183 +#define _PUSH_NULL_r01 1184 +#define _PUSH_NULL_r12 1185 +#define _PUSH_NULL_r23 1186 +#define _PUSH_NULL_CONDITIONAL_r00 1187 +#define _PY_FRAME_GENERAL_r01 1188 +#define _PY_FRAME_KW_r11 1189 +#define _QUICKEN_RESUME_r00 1190 +#define _QUICKEN_RESUME_r11 1191 +#define _QUICKEN_RESUME_r22 1192 +#define _QUICKEN_RESUME_r33 1193 +#define _REPLACE_WITH_TRUE_r11 1194 +#define _RESUME_CHECK_r00 1195 +#define _RESUME_CHECK_r11 1196 +#define _RESUME_CHECK_r22 1197 +#define _RESUME_CHECK_r33 1198 +#define _RETURN_GENERATOR_r01 1199 +#define _RETURN_VALUE_r11 1200 +#define _SAVE_RETURN_OFFSET_r00 1201 +#define _SAVE_RETURN_OFFSET_r11 1202 +#define _SAVE_RETURN_OFFSET_r22 1203 +#define _SAVE_RETURN_OFFSET_r33 1204 +#define _SEND_r22 1205 +#define _SEND_GEN_FRAME_r22 1206 +#define _SETUP_ANNOTATIONS_r00 1207 +#define _SET_ADD_r10 1208 +#define _SET_FUNCTION_ATTRIBUTE_r01 1209 +#define _SET_FUNCTION_ATTRIBUTE_r11 1210 +#define _SET_FUNCTION_ATTRIBUTE_r21 1211 +#define _SET_FUNCTION_ATTRIBUTE_r32 1212 +#define _SET_IP_r00 1213 +#define _SET_IP_r11 1214 +#define _SET_IP_r22 1215 +#define _SET_IP_r33 1216 +#define _SET_UPDATE_r10 1217 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03 1218 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13 1219 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23 1220 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33 1221 +#define _SPILL_OR_RELOAD_r01 1222 +#define _SPILL_OR_RELOAD_r02 1223 +#define _SPILL_OR_RELOAD_r03 1224 +#define _SPILL_OR_RELOAD_r10 1225 +#define _SPILL_OR_RELOAD_r12 1226 +#define _SPILL_OR_RELOAD_r13 1227 +#define _SPILL_OR_RELOAD_r20 1228 +#define _SPILL_OR_RELOAD_r21 1229 +#define _SPILL_OR_RELOAD_r23 1230 +#define _SPILL_OR_RELOAD_r30 1231 +#define _SPILL_OR_RELOAD_r31 1232 +#define _SPILL_OR_RELOAD_r32 1233 +#define _START_EXECUTOR_r00 1234 +#define _STORE_ATTR_r20 1235 +#define _STORE_ATTR_INSTANCE_VALUE_r21 1236 +#define _STORE_ATTR_SLOT_r21 1237 +#define _STORE_ATTR_WITH_HINT_r21 1238 +#define _STORE_DEREF_r10 1239 +#define _STORE_FAST_r10 1240 +#define _STORE_FAST_0_r10 1241 +#define _STORE_FAST_1_r10 1242 +#define _STORE_FAST_2_r10 1243 +#define _STORE_FAST_3_r10 1244 +#define _STORE_FAST_4_r10 1245 +#define _STORE_FAST_5_r10 1246 +#define _STORE_FAST_6_r10 1247 +#define _STORE_FAST_7_r10 1248 +#define _STORE_FAST_LOAD_FAST_r11 1249 +#define _STORE_FAST_STORE_FAST_r20 1250 +#define _STORE_GLOBAL_r10 1251 +#define _STORE_NAME_r10 1252 +#define _STORE_SLICE_r30 1253 +#define _STORE_SUBSCR_r30 1254 +#define _STORE_SUBSCR_DICT_r31 1255 +#define _STORE_SUBSCR_LIST_INT_r32 1256 +#define _SWAP_r11 1257 +#define _SWAP_2_r02 1258 +#define _SWAP_2_r12 1259 +#define _SWAP_2_r22 1260 +#define _SWAP_2_r33 1261 +#define _SWAP_3_r03 1262 +#define _SWAP_3_r13 1263 +#define _SWAP_3_r23 1264 +#define _SWAP_3_r33 1265 +#define _TIER2_RESUME_CHECK_r00 1266 +#define _TIER2_RESUME_CHECK_r11 1267 +#define _TIER2_RESUME_CHECK_r22 1268 +#define _TIER2_RESUME_CHECK_r33 1269 +#define _TO_BOOL_r11 1270 +#define _TO_BOOL_BOOL_r01 1271 +#define _TO_BOOL_BOOL_r11 1272 +#define _TO_BOOL_BOOL_r22 1273 +#define _TO_BOOL_BOOL_r33 1274 +#define _TO_BOOL_INT_r11 1275 +#define _TO_BOOL_LIST_r11 1276 +#define _TO_BOOL_NONE_r01 1277 +#define _TO_BOOL_NONE_r11 1278 +#define _TO_BOOL_NONE_r22 1279 +#define _TO_BOOL_NONE_r33 1280 +#define _TO_BOOL_STR_r11 1281 +#define _TRACE_RECORD_r00 1282 +#define _UNARY_INVERT_r11 1283 +#define _UNARY_NEGATIVE_r11 1284 +#define _UNARY_NOT_r01 1285 +#define _UNARY_NOT_r11 1286 +#define _UNARY_NOT_r22 1287 +#define _UNARY_NOT_r33 1288 +#define _UNPACK_EX_r10 1289 +#define _UNPACK_SEQUENCE_r10 1290 +#define _UNPACK_SEQUENCE_LIST_r10 1291 +#define _UNPACK_SEQUENCE_TUPLE_r10 1292 +#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1293 +#define _WITH_EXCEPT_START_r33 1294 +#define _YIELD_VALUE_r11 1295 +#define MAX_UOP_REGS_ID 1295 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 3a6bf7b3d76..1281eeb041d 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -335,6 +335,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = { [_POP_TWO_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG, [_POP_CALL_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG, [_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG, + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW] = 0, [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG, [_LOAD_CONST_UNDER_INLINE] = 0, [_LOAD_CONST_UNDER_INLINE_BORROW] = 0, @@ -3065,6 +3066,15 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { { 1, 3, _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 }, }, }, + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW] = { + .best = { 0, 1, 2, 3 }, + .entries = { + { 3, 0, _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03 }, + { 3, 1, _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13 }, + { 3, 2, _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23 }, + { 3, 3, _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33 }, + }, + }, [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = { .best = { 3, 3, 3, 3 }, .entries = { @@ -3817,6 +3827,10 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_POP_TWO_LOAD_CONST_INLINE_BORROW_r21] = _POP_TWO_LOAD_CONST_INLINE_BORROW, [_POP_CALL_LOAD_CONST_INLINE_BORROW_r21] = _POP_CALL_LOAD_CONST_INLINE_BORROW, [_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31] = _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03] = _SHUFFLE_3_LOAD_CONST_INLINE_BORROW, + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13] = _SHUFFLE_3_LOAD_CONST_INLINE_BORROW, + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23] = _SHUFFLE_3_LOAD_CONST_INLINE_BORROW, + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33] = _SHUFFLE_3_LOAD_CONST_INLINE_BORROW, [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31] = _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, [_LOAD_CONST_UNDER_INLINE_r02] = _LOAD_CONST_UNDER_INLINE, [_LOAD_CONST_UNDER_INLINE_r12] = _LOAD_CONST_UNDER_INLINE, @@ -4762,6 +4776,11 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_SET_IP_r33] = "_SET_IP_r33", [_SET_UPDATE] = "_SET_UPDATE", [_SET_UPDATE_r10] = "_SET_UPDATE_r10", + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW] = "_SHUFFLE_3_LOAD_CONST_INLINE_BORROW", + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03] = "_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03", + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13] = "_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13", + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23] = "_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23", + [_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33] = "_SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33", [_SPILL_OR_RELOAD] = "_SPILL_OR_RELOAD", [_SPILL_OR_RELOAD_r01] = "_SPILL_OR_RELOAD_r01", [_SPILL_OR_RELOAD_r02] = "_SPILL_OR_RELOAD_r02", @@ -5479,6 +5498,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 2; case _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW: return 3; + case _SHUFFLE_3_LOAD_CONST_INLINE_BORROW: + return 3; case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW: return 4; case _LOAD_CONST_UNDER_INLINE: diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 88ac1664fe5..9a822834c14 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -3008,6 +3008,18 @@ class TestUopsOptimization(unittest.TestCase): for _ in range(TIER2_THRESHOLD+1): obj.attr = EvilAttr(obj.__dict__) + def test_constant_fold_tuple(self): + def testfunc(n): + for _ in range(n): + t = (1,) + p = len(t) + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertNotIn("_CALL_LEN", uops) + def test_binary_subscr_list_int(self): def testfunc(n): l = [1] diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-19-00-59-29.gh-issue-142961.q8WRSq.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-19-00-59-29.gh-issue-142961.q8WRSq.rst new file mode 100644 index 00000000000..4b75ab232d5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-19-00-59-29.gh-issue-142961.q8WRSq.rst @@ -0,0 +1 @@ +Fix a segfault in the JIT when constant folding ``len(tuple)``. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 4ce7968ddc2..1291fe56a59 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5285,6 +5285,13 @@ dummy_func( value = PyStackRef_FromPyObjectBorrow(ptr); } + tier2 op(_SHUFFLE_3_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null, arg -- res, a, c)) { + res = PyStackRef_FromPyObjectBorrow(ptr); + a = arg; + c = callable; + INPUTS_DEAD(); + } + tier2 op(_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null, pop1, pop2 -- value)) { PyStackRef_CLOSE(pop2); PyStackRef_CLOSE(pop1); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 74bd939c87d..2305df6ad5a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -16532,6 +16532,106 @@ break; } + case _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + _PyStackRef arg; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef a; + _PyStackRef c; + arg = stack_pointer[-1]; + callable = stack_pointer[-3]; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + res = PyStackRef_FromPyObjectBorrow(ptr); + a = arg; + c = callable; + _tos_cache2 = c; + _tos_cache1 = a; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + break; + } + + case _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + _PyStackRef arg; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef a; + _PyStackRef c; + _PyStackRef _stack_item_0 = _tos_cache0; + arg = _stack_item_0; + callable = stack_pointer[-2]; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + res = PyStackRef_FromPyObjectBorrow(ptr); + a = arg; + c = callable; + _tos_cache2 = c; + _tos_cache1 = a; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + break; + } + + case _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + _PyStackRef arg; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef a; + _PyStackRef c; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + arg = _stack_item_1; + callable = stack_pointer[-1]; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + res = PyStackRef_FromPyObjectBorrow(ptr); + a = arg; + c = callable; + _tos_cache2 = c; + _tos_cache1 = a; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + break; + } + + case _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + _PyStackRef arg; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef a; + _PyStackRef c; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + arg = _stack_item_2; + callable = _stack_item_0; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + res = PyStackRef_FromPyObjectBorrow(ptr); + a = arg; + c = callable; + _tos_cache2 = c; + _tos_cache1 = a; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + break; + } + case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31: { CHECK_CURRENT_CACHED_VALUES(3); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index f36139db954..d2527fd85e3 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -529,10 +529,6 @@ dummy_func(void) { value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); } - op(_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused, unused -- value)) { - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); - } - op(_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused, unused, unused -- value)) { value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); } @@ -1263,7 +1259,7 @@ dummy_func(void) { goto error; } if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, + REPLACE_OP(this_instr, _SHUFFLE_3_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); } res = sym_new_const(ctx, temp); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 69a2a32e4f1..0389c6192e1 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2862,7 +2862,7 @@ goto error; } if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, + REPLACE_OP(this_instr, _SHUFFLE_3_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); } res = sym_new_const(ctx, temp); @@ -3424,8 +3424,7 @@ case _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW: { JitOptRef value; - PyObject *ptr = (PyObject *)this_instr->operand0; - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); + value = sym_new_not_null(ctx); CHECK_STACK_BOUNDS(-2); stack_pointer[-3] = value; stack_pointer += -2; @@ -3433,6 +3432,19 @@ break; } + case _SHUFFLE_3_LOAD_CONST_INLINE_BORROW: { + JitOptRef res; + JitOptRef a; + JitOptRef c; + res = sym_new_not_null(ctx); + a = sym_new_not_null(ctx); + c = sym_new_not_null(ctx); + stack_pointer[-3] = res; + stack_pointer[-2] = a; + stack_pointer[-1] = c; + break; + } + case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW: { JitOptRef value; PyObject *ptr = (PyObject *)this_instr->operand0; From 6a4f10325d58deb1906b39d68dc8e84f4c2bf5a4 Mon Sep 17 00:00:00 2001 From: stratakis Date: Fri, 19 Dec 2025 19:14:52 +0100 Subject: [PATCH 51/80] gh-142776: Ensure fp file descriptor is closed on all code paths in import.c (GH-142777) --- .../2025-12-18-01-00-14.gh-issue-142776.ACaoeP.rst | 1 + Python/import.c | 14 ++++---------- 2 files changed, 5 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-18-01-00-14.gh-issue-142776.ACaoeP.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-18-01-00-14.gh-issue-142776.ACaoeP.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-18-01-00-14.gh-issue-142776.ACaoeP.rst new file mode 100644 index 00000000000..3039b04d89c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-18-01-00-14.gh-issue-142776.ACaoeP.rst @@ -0,0 +1 @@ +Fix a file descriptor leak in import.c diff --git a/Python/import.c b/Python/import.c index db433dbc971..466c5868ab7 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4762,6 +4762,7 @@ static PyObject * _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) /*[clinic end generated code: output=83249b827a4fde77 input=c31b954f4cf4e09d]*/ { + FILE *fp = NULL; PyObject *mod = NULL; PyThreadState *tstate = _PyThreadState_GET(); @@ -4804,16 +4805,12 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) /* We would move this (and the fclose() below) into * _PyImport_GetModuleExportHooks(), but it isn't clear if the intervening * code relies on fp still being open. */ - FILE *fp; if (file != NULL) { fp = Py_fopen(info.filename, "r"); if (fp == NULL) { goto finally; } } - else { - fp = NULL; - } PyModInitFunction p0 = NULL; PyModExportFunction ex0 = NULL; @@ -4822,7 +4819,7 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) mod = import_run_modexport(tstate, ex0, &info, spec); // Modules created from slots handle GIL enablement (Py_mod_gil slot) // when they're created. - goto cleanup; + goto finally; } if (p0 == NULL) { goto finally; @@ -4845,13 +4842,10 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } #endif -cleanup: - // XXX Shouldn't this happen in the error cases too (i.e. in "finally")? - if (fp) { +finally: + if (fp != NULL) { fclose(fp); } - -finally: _Py_ext_module_loader_info_clear(&info); return mod; } From 6b4bc6e6a240966cfec488c25c83c40a101a94c8 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Sat, 20 Dec 2025 03:06:34 +0800 Subject: [PATCH 52/80] gh-134584: JIT: Borrow references for immortal promoted globals (GH-142921) JIT: Borrow references for immortal promoted globals --- Lib/test/test_capi/test_opt.py | 38 ++++++++++++++++++++++++++++++++++ Python/optimizer_bytecodes.c | 14 +++++++++++-- Python/optimizer_cases.c.h | 14 +++++++++++-- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 9a822834c14..3ea93277dab 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -3008,6 +3008,44 @@ class TestUopsOptimization(unittest.TestCase): for _ in range(TIER2_THRESHOLD+1): obj.attr = EvilAttr(obj.__dict__) + def test_promoted_global_refcount_eliminated(self): + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + import _testinternalcapi + import opcode + import _opcode + + def get_first_executor(func): + code = func.__code__ + co_code = code.co_code + for i in range(0, len(co_code), 2): + try: + return _opcode.get_executor(code, i) + except ValueError: + pass + return None + + def get_opnames(ex): + return {item[0] for item in ex} + + + def testfunc(n): + y = [] + for i in range(n): + x = tuple(y) + return x + + testfunc(_testinternalcapi.TIER2_THRESHOLD) + + ex = get_first_executor(testfunc) + assert ex is not None + uops = get_opnames(ex) + assert "_LOAD_GLOBAL_BUILTIN" not in uops + assert "_LOAD_CONST_INLINE_BORROW" in uops + assert "_POP_TOP_NOP" in uops + assert "_POP_TOP" not in uops + """), PYTHON_JIT="1") + self.assertEqual(result[0].rc, 0, result) + def test_constant_fold_tuple(self): def testfunc(n): for _ in range(n): diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index d2527fd85e3..cd789db1fe8 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -1376,7 +1376,12 @@ dummy_func(void) { res = sym_new_not_null(ctx); } else { - res = sym_new_const(ctx, cnst); + if (_Py_IsImmortal(cnst)) { + res = PyJitRef_Borrow(sym_new_const(ctx, cnst)); + } + else { + res = sym_new_const(ctx, cnst); + } } } @@ -1411,7 +1416,12 @@ dummy_func(void) { res = sym_new_not_null(ctx); } else { - res = sym_new_const(ctx, cnst); + if (_Py_IsImmortal(cnst)) { + res = PyJitRef_Borrow(sym_new_const(ctx, cnst)); + } + else { + res = sym_new_const(ctx, cnst); + } } } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 0389c6192e1..85d3d041215 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1330,7 +1330,12 @@ res = sym_new_not_null(ctx); } else { - res = sym_new_const(ctx, cnst); + if (_Py_IsImmortal(cnst)) { + res = PyJitRef_Borrow(sym_new_const(ctx, cnst)); + } + else { + res = sym_new_const(ctx, cnst); + } } CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; @@ -1367,7 +1372,12 @@ res = sym_new_not_null(ctx); } else { - res = sym_new_const(ctx, cnst); + if (_Py_IsImmortal(cnst)) { + res = PyJitRef_Borrow(sym_new_const(ctx, cnst)); + } + else { + res = sym_new_const(ctx, cnst); + } } CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; From e2a7db717507a66b49bb796391530147b3acab93 Mon Sep 17 00:00:00 2001 From: Shamil Date: Fri, 19 Dec 2025 22:07:11 +0300 Subject: [PATCH 53/80] gh-142476: fix memory leak when creating JIT executors (GH-142492) --- .../2025-12-13-01-11-03.gh-issue-142476.44Sp4N.rst | 2 ++ Python/optimizer.c | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-13-01-11-03.gh-issue-142476.44Sp4N.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-13-01-11-03.gh-issue-142476.44Sp4N.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-13-01-11-03.gh-issue-142476.44Sp4N.rst new file mode 100644 index 00000000000..eae1f3a1ce5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-13-01-11-03.gh-issue-142476.44Sp4N.rst @@ -0,0 +1,2 @@ +Fix a memory leak in the experimental Tier 2 optimizer when creating +executors. Patched by Shamil Abdulaev. diff --git a/Python/optimizer.c b/Python/optimizer.c index 1e905850e3d..16abced6edb 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -185,12 +185,17 @@ _PyOptimizer_Optimize( else { executor->vm_data.code = NULL; } + executor->vm_data.chain_depth = chain_depth; + assert(executor->vm_data.valid); _PyExitData *exit = _tstate->jit_tracer_state.initial_state.exit; if (exit != NULL) { exit->executor = executor; } - executor->vm_data.chain_depth = chain_depth; - assert(executor->vm_data.valid); + else { + // An executor inserted into the code object now has a strong reference + // to it from the code object. Thus, we don't need this reference anymore. + Py_DECREF(executor); + } interp->compiling = false; return 1; #else From 08bc03ff2a5545a165ba57f4ca73b8ff705dc757 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 19 Dec 2025 14:10:37 -0500 Subject: [PATCH 54/80] gh-120321: Make gi_frame_state transitions atomic in FT build (gh-142599) This makes generator frame state transitions atomic in the free threading build, which avoids segfaults when trying to execute a generator from multiple threads concurrently. There are still a few operations that aren't thread-safe and may crash if performed concurrently on the same generator/coroutine: * Accessing gi_yieldfrom/cr_await/ag_await * Accessing gi_frame/cr_frame/ag_frame * Async generator operations --- Include/cpython/pyatomic.h | 3 + Include/cpython/pyatomic_gcc.h | 4 + Include/cpython/pyatomic_msc.h | 13 + Include/cpython/pyatomic_std.h | 8 + .../internal/pycore_pyatomic_ft_wrappers.h | 9 + Include/internal/pycore_tstate.h | 7 + Include/internal/pycore_uop_ids.h | 1134 +++++++++-------- Include/internal/pycore_uop_metadata.h | 10 +- .../test_free_threading/test_generators.py | 71 ++ Objects/genobject.c | 572 +++++---- Python/bytecodes.c | 19 +- Python/ceval.c | 15 +- Python/ceval_macros.h | 25 + Python/executor_cases.c.h | 94 +- Python/generated_cases.c.h | 22 +- Tools/cases_generator/analyzer.py | 1 + 16 files changed, 1124 insertions(+), 883 deletions(-) diff --git a/Include/cpython/pyatomic.h b/Include/cpython/pyatomic.h index 790640309f1..ce907fd6a4c 100644 --- a/Include/cpython/pyatomic.h +++ b/Include/cpython/pyatomic.h @@ -523,6 +523,9 @@ _Py_atomic_store_uintptr_release(uintptr_t *obj, uintptr_t value); static inline void _Py_atomic_store_ssize_release(Py_ssize_t *obj, Py_ssize_t value); +static inline void +_Py_atomic_store_int8_release(int8_t *obj, int8_t value); + static inline void _Py_atomic_store_int_release(int *obj, int value); diff --git a/Include/cpython/pyatomic_gcc.h b/Include/cpython/pyatomic_gcc.h index 1566b83b9f6..c045213c898 100644 --- a/Include/cpython/pyatomic_gcc.h +++ b/Include/cpython/pyatomic_gcc.h @@ -572,6 +572,10 @@ static inline void _Py_atomic_store_int_release(int *obj, int value) { __atomic_store_n(obj, value, __ATOMIC_RELEASE); } +static inline void +_Py_atomic_store_int8_release(int8_t *obj, int8_t value) +{ __atomic_store_n(obj, value, __ATOMIC_RELEASE); } + static inline void _Py_atomic_store_ssize_release(Py_ssize_t *obj, Py_ssize_t value) { __atomic_store_n(obj, value, __ATOMIC_RELEASE); } diff --git a/Include/cpython/pyatomic_msc.h b/Include/cpython/pyatomic_msc.h index d155955df0c..8b9dd3eb0f8 100644 --- a/Include/cpython/pyatomic_msc.h +++ b/Include/cpython/pyatomic_msc.h @@ -1066,6 +1066,19 @@ _Py_atomic_store_int_release(int *obj, int value) #endif } +static inline void +_Py_atomic_store_int8_release(int8_t *obj, int8_t value) +{ +#if defined(_M_X64) || defined(_M_IX86) + *(int8_t volatile *)obj = value; +#elif defined(_M_ARM64) + _Py_atomic_ASSERT_ARG_TYPE(unsigned __int8); + __stlr8((unsigned __int8 volatile *)obj, (unsigned __int8)value); +#else +# error "no implementation of _Py_atomic_store_int8_release" +#endif +} + static inline void _Py_atomic_store_ssize_release(Py_ssize_t *obj, Py_ssize_t value) { diff --git a/Include/cpython/pyatomic_std.h b/Include/cpython/pyatomic_std.h index 7176f667a40..cfc8dbefc63 100644 --- a/Include/cpython/pyatomic_std.h +++ b/Include/cpython/pyatomic_std.h @@ -1023,6 +1023,14 @@ _Py_atomic_store_int_release(int *obj, int value) memory_order_release); } +static inline void +_Py_atomic_store_int8_release(int8_t *obj, int8_t value) +{ + _Py_USING_STD; + atomic_store_explicit((_Atomic(int8_t)*)obj, value, + memory_order_release); +} + static inline void _Py_atomic_store_uint_release(unsigned int *obj, unsigned int value) { diff --git a/Include/internal/pycore_pyatomic_ft_wrappers.h b/Include/internal/pycore_pyatomic_ft_wrappers.h index 1a6d5075361..70a32db663b 100644 --- a/Include/internal/pycore_pyatomic_ft_wrappers.h +++ b/Include/internal/pycore_pyatomic_ft_wrappers.h @@ -41,6 +41,8 @@ extern "C" { _Py_atomic_load_uint8(&value) #define FT_ATOMIC_STORE_UINT8(value, new_value) \ _Py_atomic_store_uint8(&value, new_value) +#define FT_ATOMIC_LOAD_INT8_RELAXED(value) \ + _Py_atomic_load_int8_relaxed(&value) #define FT_ATOMIC_LOAD_UINT8_RELAXED(value) \ _Py_atomic_load_uint8_relaxed(&value) #define FT_ATOMIC_LOAD_UINT16_RELAXED(value) \ @@ -55,6 +57,10 @@ extern "C" { _Py_atomic_store_ptr_release(&value, new_value) #define FT_ATOMIC_STORE_UINTPTR_RELEASE(value, new_value) \ _Py_atomic_store_uintptr_release(&value, new_value) +#define FT_ATOMIC_STORE_INT8_RELAXED(value, new_value) \ + _Py_atomic_store_int8_relaxed(&value, new_value) +#define FT_ATOMIC_STORE_INT8_RELEASE(value, new_value) \ + _Py_atomic_store_int8_release(&value, new_value) #define FT_ATOMIC_STORE_SSIZE_RELAXED(value, new_value) \ _Py_atomic_store_ssize_relaxed(&value, new_value) #define FT_ATOMIC_STORE_SSIZE_RELEASE(value, new_value) \ @@ -134,6 +140,7 @@ extern "C" { #define FT_ATOMIC_LOAD_PTR_RELAXED(value) value #define FT_ATOMIC_LOAD_UINT8(value) value #define FT_ATOMIC_STORE_UINT8(value, new_value) value = new_value +#define FT_ATOMIC_LOAD_INT8_RELAXED(value) value #define FT_ATOMIC_LOAD_UINT8_RELAXED(value) value #define FT_ATOMIC_LOAD_UINT16_RELAXED(value) value #define FT_ATOMIC_LOAD_UINT32_RELAXED(value) value @@ -141,6 +148,8 @@ extern "C" { #define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) value = new_value #define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) value = new_value #define FT_ATOMIC_STORE_UINTPTR_RELEASE(value, new_value) value = new_value +#define FT_ATOMIC_STORE_INT8_RELAXED(value, new_value) value = new_value +#define FT_ATOMIC_STORE_INT8_RELEASE(value, new_value) value = new_value #define FT_ATOMIC_STORE_SSIZE_RELAXED(value, new_value) value = new_value #define FT_ATOMIC_STORE_SSIZE_RELEASE(value, new_value) value = new_value #define FT_ATOMIC_STORE_UINT8_RELAXED(value, new_value) value = new_value diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index c4f723ac8ab..a57f1f45c13 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -82,6 +82,13 @@ typedef struct _PyThreadStateImpl { PyObject *asyncio_running_loop; // Strong reference PyObject *asyncio_running_task; // Strong reference + // Distinguishes between yield and return from PyEval_EvalFrame(). + // See gen_send_ex2() in Objects/genobject.c + enum { + GENERATOR_RETURN = 0, + GENERATOR_YIELD = 1, + } generator_return_kind; + /* Head of circular linked-list of all tasks which are instances of `asyncio.Task` or subclasses of it used in `asyncio.all_tasks`. */ diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 6130e5cad21..a11959fb405 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -535,572 +535,574 @@ extern "C" { #define _FORMAT_SIMPLE_r11 728 #define _FORMAT_WITH_SPEC_r21 729 #define _FOR_ITER_r23 730 -#define _FOR_ITER_GEN_FRAME_r23 731 -#define _FOR_ITER_TIER_TWO_r23 732 -#define _GET_AITER_r11 733 -#define _GET_ANEXT_r12 734 -#define _GET_AWAITABLE_r11 735 -#define _GET_ITER_r12 736 -#define _GET_LEN_r12 737 -#define _GET_YIELD_FROM_ITER_r11 738 -#define _GUARD_BINARY_OP_EXTEND_r22 739 -#define _GUARD_CALLABLE_ISINSTANCE_r03 740 -#define _GUARD_CALLABLE_ISINSTANCE_r13 741 -#define _GUARD_CALLABLE_ISINSTANCE_r23 742 -#define _GUARD_CALLABLE_ISINSTANCE_r33 743 -#define _GUARD_CALLABLE_LEN_r03 744 -#define _GUARD_CALLABLE_LEN_r13 745 -#define _GUARD_CALLABLE_LEN_r23 746 -#define _GUARD_CALLABLE_LEN_r33 747 -#define _GUARD_CALLABLE_LIST_APPEND_r03 748 -#define _GUARD_CALLABLE_LIST_APPEND_r13 749 -#define _GUARD_CALLABLE_LIST_APPEND_r23 750 -#define _GUARD_CALLABLE_LIST_APPEND_r33 751 -#define _GUARD_CALLABLE_STR_1_r03 752 -#define _GUARD_CALLABLE_STR_1_r13 753 -#define _GUARD_CALLABLE_STR_1_r23 754 -#define _GUARD_CALLABLE_STR_1_r33 755 -#define _GUARD_CALLABLE_TUPLE_1_r03 756 -#define _GUARD_CALLABLE_TUPLE_1_r13 757 -#define _GUARD_CALLABLE_TUPLE_1_r23 758 -#define _GUARD_CALLABLE_TUPLE_1_r33 759 -#define _GUARD_CALLABLE_TYPE_1_r03 760 -#define _GUARD_CALLABLE_TYPE_1_r13 761 -#define _GUARD_CALLABLE_TYPE_1_r23 762 -#define _GUARD_CALLABLE_TYPE_1_r33 763 -#define _GUARD_DORV_NO_DICT_r01 764 -#define _GUARD_DORV_NO_DICT_r11 765 -#define _GUARD_DORV_NO_DICT_r22 766 -#define _GUARD_DORV_NO_DICT_r33 767 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r01 768 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11 769 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22 770 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33 771 -#define _GUARD_GLOBALS_VERSION_r00 772 -#define _GUARD_GLOBALS_VERSION_r11 773 -#define _GUARD_GLOBALS_VERSION_r22 774 -#define _GUARD_GLOBALS_VERSION_r33 775 -#define _GUARD_IP_RETURN_GENERATOR_r00 776 -#define _GUARD_IP_RETURN_GENERATOR_r11 777 -#define _GUARD_IP_RETURN_GENERATOR_r22 778 -#define _GUARD_IP_RETURN_GENERATOR_r33 779 -#define _GUARD_IP_RETURN_VALUE_r00 780 -#define _GUARD_IP_RETURN_VALUE_r11 781 -#define _GUARD_IP_RETURN_VALUE_r22 782 -#define _GUARD_IP_RETURN_VALUE_r33 783 -#define _GUARD_IP_YIELD_VALUE_r00 784 -#define _GUARD_IP_YIELD_VALUE_r11 785 -#define _GUARD_IP_YIELD_VALUE_r22 786 -#define _GUARD_IP_YIELD_VALUE_r33 787 -#define _GUARD_IP__PUSH_FRAME_r00 788 -#define _GUARD_IP__PUSH_FRAME_r11 789 -#define _GUARD_IP__PUSH_FRAME_r22 790 -#define _GUARD_IP__PUSH_FRAME_r33 791 -#define _GUARD_IS_FALSE_POP_r00 792 -#define _GUARD_IS_FALSE_POP_r10 793 -#define _GUARD_IS_FALSE_POP_r21 794 -#define _GUARD_IS_FALSE_POP_r32 795 -#define _GUARD_IS_NONE_POP_r00 796 -#define _GUARD_IS_NONE_POP_r10 797 -#define _GUARD_IS_NONE_POP_r21 798 -#define _GUARD_IS_NONE_POP_r32 799 -#define _GUARD_IS_NOT_NONE_POP_r10 800 -#define _GUARD_IS_TRUE_POP_r00 801 -#define _GUARD_IS_TRUE_POP_r10 802 -#define _GUARD_IS_TRUE_POP_r21 803 -#define _GUARD_IS_TRUE_POP_r32 804 -#define _GUARD_KEYS_VERSION_r01 805 -#define _GUARD_KEYS_VERSION_r11 806 -#define _GUARD_KEYS_VERSION_r22 807 -#define _GUARD_KEYS_VERSION_r33 808 -#define _GUARD_NOS_DICT_r02 809 -#define _GUARD_NOS_DICT_r12 810 -#define _GUARD_NOS_DICT_r22 811 -#define _GUARD_NOS_DICT_r33 812 -#define _GUARD_NOS_FLOAT_r02 813 -#define _GUARD_NOS_FLOAT_r12 814 -#define _GUARD_NOS_FLOAT_r22 815 -#define _GUARD_NOS_FLOAT_r33 816 -#define _GUARD_NOS_INT_r02 817 -#define _GUARD_NOS_INT_r12 818 -#define _GUARD_NOS_INT_r22 819 -#define _GUARD_NOS_INT_r33 820 -#define _GUARD_NOS_LIST_r02 821 -#define _GUARD_NOS_LIST_r12 822 -#define _GUARD_NOS_LIST_r22 823 -#define _GUARD_NOS_LIST_r33 824 -#define _GUARD_NOS_NOT_NULL_r02 825 -#define _GUARD_NOS_NOT_NULL_r12 826 -#define _GUARD_NOS_NOT_NULL_r22 827 -#define _GUARD_NOS_NOT_NULL_r33 828 -#define _GUARD_NOS_NULL_r02 829 -#define _GUARD_NOS_NULL_r12 830 -#define _GUARD_NOS_NULL_r22 831 -#define _GUARD_NOS_NULL_r33 832 -#define _GUARD_NOS_OVERFLOWED_r02 833 -#define _GUARD_NOS_OVERFLOWED_r12 834 -#define _GUARD_NOS_OVERFLOWED_r22 835 -#define _GUARD_NOS_OVERFLOWED_r33 836 -#define _GUARD_NOS_TUPLE_r02 837 -#define _GUARD_NOS_TUPLE_r12 838 -#define _GUARD_NOS_TUPLE_r22 839 -#define _GUARD_NOS_TUPLE_r33 840 -#define _GUARD_NOS_UNICODE_r02 841 -#define _GUARD_NOS_UNICODE_r12 842 -#define _GUARD_NOS_UNICODE_r22 843 -#define _GUARD_NOS_UNICODE_r33 844 -#define _GUARD_NOT_EXHAUSTED_LIST_r02 845 -#define _GUARD_NOT_EXHAUSTED_LIST_r12 846 -#define _GUARD_NOT_EXHAUSTED_LIST_r22 847 -#define _GUARD_NOT_EXHAUSTED_LIST_r33 848 -#define _GUARD_NOT_EXHAUSTED_RANGE_r02 849 -#define _GUARD_NOT_EXHAUSTED_RANGE_r12 850 -#define _GUARD_NOT_EXHAUSTED_RANGE_r22 851 -#define _GUARD_NOT_EXHAUSTED_RANGE_r33 852 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r02 853 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r12 854 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r22 855 -#define _GUARD_NOT_EXHAUSTED_TUPLE_r33 856 -#define _GUARD_THIRD_NULL_r03 857 -#define _GUARD_THIRD_NULL_r13 858 -#define _GUARD_THIRD_NULL_r23 859 -#define _GUARD_THIRD_NULL_r33 860 -#define _GUARD_TOS_ANY_SET_r01 861 -#define _GUARD_TOS_ANY_SET_r11 862 -#define _GUARD_TOS_ANY_SET_r22 863 -#define _GUARD_TOS_ANY_SET_r33 864 -#define _GUARD_TOS_DICT_r01 865 -#define _GUARD_TOS_DICT_r11 866 -#define _GUARD_TOS_DICT_r22 867 -#define _GUARD_TOS_DICT_r33 868 -#define _GUARD_TOS_FLOAT_r01 869 -#define _GUARD_TOS_FLOAT_r11 870 -#define _GUARD_TOS_FLOAT_r22 871 -#define _GUARD_TOS_FLOAT_r33 872 -#define _GUARD_TOS_INT_r01 873 -#define _GUARD_TOS_INT_r11 874 -#define _GUARD_TOS_INT_r22 875 -#define _GUARD_TOS_INT_r33 876 -#define _GUARD_TOS_LIST_r01 877 -#define _GUARD_TOS_LIST_r11 878 -#define _GUARD_TOS_LIST_r22 879 -#define _GUARD_TOS_LIST_r33 880 -#define _GUARD_TOS_OVERFLOWED_r01 881 -#define _GUARD_TOS_OVERFLOWED_r11 882 -#define _GUARD_TOS_OVERFLOWED_r22 883 -#define _GUARD_TOS_OVERFLOWED_r33 884 -#define _GUARD_TOS_SLICE_r01 885 -#define _GUARD_TOS_SLICE_r11 886 -#define _GUARD_TOS_SLICE_r22 887 -#define _GUARD_TOS_SLICE_r33 888 -#define _GUARD_TOS_TUPLE_r01 889 -#define _GUARD_TOS_TUPLE_r11 890 -#define _GUARD_TOS_TUPLE_r22 891 -#define _GUARD_TOS_TUPLE_r33 892 -#define _GUARD_TOS_UNICODE_r01 893 -#define _GUARD_TOS_UNICODE_r11 894 -#define _GUARD_TOS_UNICODE_r22 895 -#define _GUARD_TOS_UNICODE_r33 896 -#define _GUARD_TYPE_VERSION_r01 897 -#define _GUARD_TYPE_VERSION_r11 898 -#define _GUARD_TYPE_VERSION_r22 899 -#define _GUARD_TYPE_VERSION_r33 900 -#define _GUARD_TYPE_VERSION_AND_LOCK_r01 901 -#define _GUARD_TYPE_VERSION_AND_LOCK_r11 902 -#define _GUARD_TYPE_VERSION_AND_LOCK_r22 903 -#define _GUARD_TYPE_VERSION_AND_LOCK_r33 904 -#define _HANDLE_PENDING_AND_DEOPT_r00 905 -#define _HANDLE_PENDING_AND_DEOPT_r10 906 -#define _HANDLE_PENDING_AND_DEOPT_r20 907 -#define _HANDLE_PENDING_AND_DEOPT_r30 908 -#define _IMPORT_FROM_r12 909 -#define _IMPORT_NAME_r21 910 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS_r00 911 -#define _INIT_CALL_PY_EXACT_ARGS_r01 912 -#define _INIT_CALL_PY_EXACT_ARGS_0_r01 913 -#define _INIT_CALL_PY_EXACT_ARGS_1_r01 914 -#define _INIT_CALL_PY_EXACT_ARGS_2_r01 915 -#define _INIT_CALL_PY_EXACT_ARGS_3_r01 916 -#define _INIT_CALL_PY_EXACT_ARGS_4_r01 917 -#define _INSERT_NULL_r10 918 -#define _INSTRUMENTED_FOR_ITER_r23 919 -#define _INSTRUMENTED_INSTRUCTION_r00 920 -#define _INSTRUMENTED_JUMP_FORWARD_r00 921 -#define _INSTRUMENTED_JUMP_FORWARD_r11 922 -#define _INSTRUMENTED_JUMP_FORWARD_r22 923 -#define _INSTRUMENTED_JUMP_FORWARD_r33 924 -#define _INSTRUMENTED_LINE_r00 925 -#define _INSTRUMENTED_NOT_TAKEN_r00 926 -#define _INSTRUMENTED_NOT_TAKEN_r11 927 -#define _INSTRUMENTED_NOT_TAKEN_r22 928 -#define _INSTRUMENTED_NOT_TAKEN_r33 929 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r00 930 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r10 931 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r21 932 -#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r32 933 -#define _INSTRUMENTED_POP_JUMP_IF_NONE_r10 934 -#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE_r10 935 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r00 936 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r10 937 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r21 938 -#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r32 939 -#define _IS_NONE_r11 940 -#define _IS_OP_r21 941 -#define _ITER_CHECK_LIST_r02 942 -#define _ITER_CHECK_LIST_r12 943 -#define _ITER_CHECK_LIST_r22 944 -#define _ITER_CHECK_LIST_r33 945 -#define _ITER_CHECK_RANGE_r02 946 -#define _ITER_CHECK_RANGE_r12 947 -#define _ITER_CHECK_RANGE_r22 948 -#define _ITER_CHECK_RANGE_r33 949 -#define _ITER_CHECK_TUPLE_r02 950 -#define _ITER_CHECK_TUPLE_r12 951 -#define _ITER_CHECK_TUPLE_r22 952 -#define _ITER_CHECK_TUPLE_r33 953 -#define _ITER_JUMP_LIST_r02 954 -#define _ITER_JUMP_LIST_r12 955 -#define _ITER_JUMP_LIST_r22 956 -#define _ITER_JUMP_LIST_r33 957 -#define _ITER_JUMP_RANGE_r02 958 -#define _ITER_JUMP_RANGE_r12 959 -#define _ITER_JUMP_RANGE_r22 960 -#define _ITER_JUMP_RANGE_r33 961 -#define _ITER_JUMP_TUPLE_r02 962 -#define _ITER_JUMP_TUPLE_r12 963 -#define _ITER_JUMP_TUPLE_r22 964 -#define _ITER_JUMP_TUPLE_r33 965 -#define _ITER_NEXT_LIST_r23 966 -#define _ITER_NEXT_LIST_TIER_TWO_r23 967 -#define _ITER_NEXT_RANGE_r03 968 -#define _ITER_NEXT_RANGE_r13 969 -#define _ITER_NEXT_RANGE_r23 970 -#define _ITER_NEXT_TUPLE_r03 971 -#define _ITER_NEXT_TUPLE_r13 972 -#define _ITER_NEXT_TUPLE_r23 973 -#define _JUMP_BACKWARD_NO_INTERRUPT_r00 974 -#define _JUMP_BACKWARD_NO_INTERRUPT_r11 975 -#define _JUMP_BACKWARD_NO_INTERRUPT_r22 976 -#define _JUMP_BACKWARD_NO_INTERRUPT_r33 977 -#define _JUMP_TO_TOP_r00 978 -#define _LIST_APPEND_r10 979 -#define _LIST_EXTEND_r10 980 -#define _LOAD_ATTR_r10 981 -#define _LOAD_ATTR_CLASS_r11 982 -#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_r11 983 -#define _LOAD_ATTR_INSTANCE_VALUE_r02 984 -#define _LOAD_ATTR_INSTANCE_VALUE_r12 985 -#define _LOAD_ATTR_INSTANCE_VALUE_r23 986 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 987 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 988 -#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 989 -#define _LOAD_ATTR_METHOD_NO_DICT_r02 990 -#define _LOAD_ATTR_METHOD_NO_DICT_r12 991 -#define _LOAD_ATTR_METHOD_NO_DICT_r23 992 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 993 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 994 -#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 995 -#define _LOAD_ATTR_MODULE_r11 996 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 997 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 998 -#define _LOAD_ATTR_PROPERTY_FRAME_r11 999 -#define _LOAD_ATTR_SLOT_r11 1000 -#define _LOAD_ATTR_WITH_HINT_r11 1001 -#define _LOAD_BUILD_CLASS_r01 1002 -#define _LOAD_BYTECODE_r00 1003 -#define _LOAD_COMMON_CONSTANT_r01 1004 -#define _LOAD_COMMON_CONSTANT_r12 1005 -#define _LOAD_COMMON_CONSTANT_r23 1006 -#define _LOAD_CONST_r01 1007 -#define _LOAD_CONST_r12 1008 -#define _LOAD_CONST_r23 1009 -#define _LOAD_CONST_INLINE_r01 1010 -#define _LOAD_CONST_INLINE_r12 1011 -#define _LOAD_CONST_INLINE_r23 1012 -#define _LOAD_CONST_INLINE_BORROW_r01 1013 -#define _LOAD_CONST_INLINE_BORROW_r12 1014 -#define _LOAD_CONST_INLINE_BORROW_r23 1015 -#define _LOAD_CONST_UNDER_INLINE_r02 1016 -#define _LOAD_CONST_UNDER_INLINE_r12 1017 -#define _LOAD_CONST_UNDER_INLINE_r23 1018 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1019 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1020 -#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1021 -#define _LOAD_DEREF_r01 1022 -#define _LOAD_FAST_r01 1023 -#define _LOAD_FAST_r12 1024 -#define _LOAD_FAST_r23 1025 -#define _LOAD_FAST_0_r01 1026 -#define _LOAD_FAST_0_r12 1027 -#define _LOAD_FAST_0_r23 1028 -#define _LOAD_FAST_1_r01 1029 -#define _LOAD_FAST_1_r12 1030 -#define _LOAD_FAST_1_r23 1031 -#define _LOAD_FAST_2_r01 1032 -#define _LOAD_FAST_2_r12 1033 -#define _LOAD_FAST_2_r23 1034 -#define _LOAD_FAST_3_r01 1035 -#define _LOAD_FAST_3_r12 1036 -#define _LOAD_FAST_3_r23 1037 -#define _LOAD_FAST_4_r01 1038 -#define _LOAD_FAST_4_r12 1039 -#define _LOAD_FAST_4_r23 1040 -#define _LOAD_FAST_5_r01 1041 -#define _LOAD_FAST_5_r12 1042 -#define _LOAD_FAST_5_r23 1043 -#define _LOAD_FAST_6_r01 1044 -#define _LOAD_FAST_6_r12 1045 -#define _LOAD_FAST_6_r23 1046 -#define _LOAD_FAST_7_r01 1047 -#define _LOAD_FAST_7_r12 1048 -#define _LOAD_FAST_7_r23 1049 -#define _LOAD_FAST_AND_CLEAR_r01 1050 -#define _LOAD_FAST_AND_CLEAR_r12 1051 -#define _LOAD_FAST_AND_CLEAR_r23 1052 -#define _LOAD_FAST_BORROW_r01 1053 -#define _LOAD_FAST_BORROW_r12 1054 -#define _LOAD_FAST_BORROW_r23 1055 -#define _LOAD_FAST_BORROW_0_r01 1056 -#define _LOAD_FAST_BORROW_0_r12 1057 -#define _LOAD_FAST_BORROW_0_r23 1058 -#define _LOAD_FAST_BORROW_1_r01 1059 -#define _LOAD_FAST_BORROW_1_r12 1060 -#define _LOAD_FAST_BORROW_1_r23 1061 -#define _LOAD_FAST_BORROW_2_r01 1062 -#define _LOAD_FAST_BORROW_2_r12 1063 -#define _LOAD_FAST_BORROW_2_r23 1064 -#define _LOAD_FAST_BORROW_3_r01 1065 -#define _LOAD_FAST_BORROW_3_r12 1066 -#define _LOAD_FAST_BORROW_3_r23 1067 -#define _LOAD_FAST_BORROW_4_r01 1068 -#define _LOAD_FAST_BORROW_4_r12 1069 -#define _LOAD_FAST_BORROW_4_r23 1070 -#define _LOAD_FAST_BORROW_5_r01 1071 -#define _LOAD_FAST_BORROW_5_r12 1072 -#define _LOAD_FAST_BORROW_5_r23 1073 -#define _LOAD_FAST_BORROW_6_r01 1074 -#define _LOAD_FAST_BORROW_6_r12 1075 -#define _LOAD_FAST_BORROW_6_r23 1076 -#define _LOAD_FAST_BORROW_7_r01 1077 -#define _LOAD_FAST_BORROW_7_r12 1078 -#define _LOAD_FAST_BORROW_7_r23 1079 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1080 -#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1081 -#define _LOAD_FAST_CHECK_r01 1082 -#define _LOAD_FAST_CHECK_r12 1083 -#define _LOAD_FAST_CHECK_r23 1084 -#define _LOAD_FAST_LOAD_FAST_r02 1085 -#define _LOAD_FAST_LOAD_FAST_r13 1086 -#define _LOAD_FROM_DICT_OR_DEREF_r11 1087 -#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1088 -#define _LOAD_GLOBAL_r00 1089 -#define _LOAD_GLOBAL_BUILTINS_r01 1090 -#define _LOAD_GLOBAL_MODULE_r01 1091 -#define _LOAD_LOCALS_r01 1092 -#define _LOAD_LOCALS_r12 1093 -#define _LOAD_LOCALS_r23 1094 -#define _LOAD_NAME_r01 1095 -#define _LOAD_SMALL_INT_r01 1096 -#define _LOAD_SMALL_INT_r12 1097 -#define _LOAD_SMALL_INT_r23 1098 -#define _LOAD_SMALL_INT_0_r01 1099 -#define _LOAD_SMALL_INT_0_r12 1100 -#define _LOAD_SMALL_INT_0_r23 1101 -#define _LOAD_SMALL_INT_1_r01 1102 -#define _LOAD_SMALL_INT_1_r12 1103 -#define _LOAD_SMALL_INT_1_r23 1104 -#define _LOAD_SMALL_INT_2_r01 1105 -#define _LOAD_SMALL_INT_2_r12 1106 -#define _LOAD_SMALL_INT_2_r23 1107 -#define _LOAD_SMALL_INT_3_r01 1108 -#define _LOAD_SMALL_INT_3_r12 1109 -#define _LOAD_SMALL_INT_3_r23 1110 -#define _LOAD_SPECIAL_r00 1111 -#define _LOAD_SUPER_ATTR_ATTR_r31 1112 -#define _LOAD_SUPER_ATTR_METHOD_r32 1113 -#define _MAKE_CALLARGS_A_TUPLE_r33 1114 -#define _MAKE_CELL_r00 1115 -#define _MAKE_FUNCTION_r11 1116 -#define _MAKE_WARM_r00 1117 -#define _MAKE_WARM_r11 1118 -#define _MAKE_WARM_r22 1119 -#define _MAKE_WARM_r33 1120 -#define _MAP_ADD_r20 1121 -#define _MATCH_CLASS_r31 1122 -#define _MATCH_KEYS_r23 1123 -#define _MATCH_MAPPING_r02 1124 -#define _MATCH_MAPPING_r12 1125 -#define _MATCH_MAPPING_r23 1126 -#define _MATCH_SEQUENCE_r02 1127 -#define _MATCH_SEQUENCE_r12 1128 -#define _MATCH_SEQUENCE_r23 1129 -#define _MAYBE_EXPAND_METHOD_r00 1130 -#define _MAYBE_EXPAND_METHOD_KW_r11 1131 -#define _MONITOR_CALL_r00 1132 -#define _MONITOR_CALL_KW_r11 1133 -#define _MONITOR_JUMP_BACKWARD_r00 1134 -#define _MONITOR_JUMP_BACKWARD_r11 1135 -#define _MONITOR_JUMP_BACKWARD_r22 1136 -#define _MONITOR_JUMP_BACKWARD_r33 1137 -#define _MONITOR_RESUME_r00 1138 -#define _NOP_r00 1139 -#define _NOP_r11 1140 -#define _NOP_r22 1141 -#define _NOP_r33 1142 -#define _POP_CALL_r20 1143 -#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1144 -#define _POP_CALL_ONE_r30 1145 -#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1146 -#define _POP_CALL_TWO_r30 1147 -#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1148 -#define _POP_EXCEPT_r10 1149 -#define _POP_ITER_r20 1150 -#define _POP_JUMP_IF_FALSE_r00 1151 -#define _POP_JUMP_IF_FALSE_r10 1152 -#define _POP_JUMP_IF_FALSE_r21 1153 -#define _POP_JUMP_IF_FALSE_r32 1154 -#define _POP_JUMP_IF_TRUE_r00 1155 -#define _POP_JUMP_IF_TRUE_r10 1156 -#define _POP_JUMP_IF_TRUE_r21 1157 -#define _POP_JUMP_IF_TRUE_r32 1158 -#define _POP_TOP_r10 1159 -#define _POP_TOP_FLOAT_r00 1160 -#define _POP_TOP_FLOAT_r10 1161 -#define _POP_TOP_FLOAT_r21 1162 -#define _POP_TOP_FLOAT_r32 1163 -#define _POP_TOP_INT_r00 1164 -#define _POP_TOP_INT_r10 1165 -#define _POP_TOP_INT_r21 1166 -#define _POP_TOP_INT_r32 1167 -#define _POP_TOP_LOAD_CONST_INLINE_r11 1168 -#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1169 -#define _POP_TOP_NOP_r00 1170 -#define _POP_TOP_NOP_r10 1171 -#define _POP_TOP_NOP_r21 1172 -#define _POP_TOP_NOP_r32 1173 -#define _POP_TOP_UNICODE_r00 1174 -#define _POP_TOP_UNICODE_r10 1175 -#define _POP_TOP_UNICODE_r21 1176 -#define _POP_TOP_UNICODE_r32 1177 -#define _POP_TWO_r20 1178 -#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1179 -#define _PUSH_EXC_INFO_r02 1180 -#define _PUSH_EXC_INFO_r12 1181 -#define _PUSH_EXC_INFO_r23 1182 -#define _PUSH_FRAME_r10 1183 -#define _PUSH_NULL_r01 1184 -#define _PUSH_NULL_r12 1185 -#define _PUSH_NULL_r23 1186 -#define _PUSH_NULL_CONDITIONAL_r00 1187 -#define _PY_FRAME_GENERAL_r01 1188 -#define _PY_FRAME_KW_r11 1189 -#define _QUICKEN_RESUME_r00 1190 -#define _QUICKEN_RESUME_r11 1191 -#define _QUICKEN_RESUME_r22 1192 -#define _QUICKEN_RESUME_r33 1193 -#define _REPLACE_WITH_TRUE_r11 1194 -#define _RESUME_CHECK_r00 1195 -#define _RESUME_CHECK_r11 1196 -#define _RESUME_CHECK_r22 1197 -#define _RESUME_CHECK_r33 1198 -#define _RETURN_GENERATOR_r01 1199 -#define _RETURN_VALUE_r11 1200 -#define _SAVE_RETURN_OFFSET_r00 1201 -#define _SAVE_RETURN_OFFSET_r11 1202 -#define _SAVE_RETURN_OFFSET_r22 1203 -#define _SAVE_RETURN_OFFSET_r33 1204 -#define _SEND_r22 1205 -#define _SEND_GEN_FRAME_r22 1206 -#define _SETUP_ANNOTATIONS_r00 1207 -#define _SET_ADD_r10 1208 -#define _SET_FUNCTION_ATTRIBUTE_r01 1209 -#define _SET_FUNCTION_ATTRIBUTE_r11 1210 -#define _SET_FUNCTION_ATTRIBUTE_r21 1211 -#define _SET_FUNCTION_ATTRIBUTE_r32 1212 -#define _SET_IP_r00 1213 -#define _SET_IP_r11 1214 -#define _SET_IP_r22 1215 -#define _SET_IP_r33 1216 -#define _SET_UPDATE_r10 1217 -#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03 1218 -#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13 1219 -#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23 1220 -#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33 1221 -#define _SPILL_OR_RELOAD_r01 1222 -#define _SPILL_OR_RELOAD_r02 1223 -#define _SPILL_OR_RELOAD_r03 1224 -#define _SPILL_OR_RELOAD_r10 1225 -#define _SPILL_OR_RELOAD_r12 1226 -#define _SPILL_OR_RELOAD_r13 1227 -#define _SPILL_OR_RELOAD_r20 1228 -#define _SPILL_OR_RELOAD_r21 1229 -#define _SPILL_OR_RELOAD_r23 1230 -#define _SPILL_OR_RELOAD_r30 1231 -#define _SPILL_OR_RELOAD_r31 1232 -#define _SPILL_OR_RELOAD_r32 1233 -#define _START_EXECUTOR_r00 1234 -#define _STORE_ATTR_r20 1235 -#define _STORE_ATTR_INSTANCE_VALUE_r21 1236 -#define _STORE_ATTR_SLOT_r21 1237 -#define _STORE_ATTR_WITH_HINT_r21 1238 -#define _STORE_DEREF_r10 1239 -#define _STORE_FAST_r10 1240 -#define _STORE_FAST_0_r10 1241 -#define _STORE_FAST_1_r10 1242 -#define _STORE_FAST_2_r10 1243 -#define _STORE_FAST_3_r10 1244 -#define _STORE_FAST_4_r10 1245 -#define _STORE_FAST_5_r10 1246 -#define _STORE_FAST_6_r10 1247 -#define _STORE_FAST_7_r10 1248 -#define _STORE_FAST_LOAD_FAST_r11 1249 -#define _STORE_FAST_STORE_FAST_r20 1250 -#define _STORE_GLOBAL_r10 1251 -#define _STORE_NAME_r10 1252 -#define _STORE_SLICE_r30 1253 -#define _STORE_SUBSCR_r30 1254 -#define _STORE_SUBSCR_DICT_r31 1255 -#define _STORE_SUBSCR_LIST_INT_r32 1256 -#define _SWAP_r11 1257 -#define _SWAP_2_r02 1258 -#define _SWAP_2_r12 1259 -#define _SWAP_2_r22 1260 -#define _SWAP_2_r33 1261 -#define _SWAP_3_r03 1262 -#define _SWAP_3_r13 1263 -#define _SWAP_3_r23 1264 -#define _SWAP_3_r33 1265 -#define _TIER2_RESUME_CHECK_r00 1266 -#define _TIER2_RESUME_CHECK_r11 1267 -#define _TIER2_RESUME_CHECK_r22 1268 -#define _TIER2_RESUME_CHECK_r33 1269 -#define _TO_BOOL_r11 1270 -#define _TO_BOOL_BOOL_r01 1271 -#define _TO_BOOL_BOOL_r11 1272 -#define _TO_BOOL_BOOL_r22 1273 -#define _TO_BOOL_BOOL_r33 1274 -#define _TO_BOOL_INT_r11 1275 -#define _TO_BOOL_LIST_r11 1276 -#define _TO_BOOL_NONE_r01 1277 -#define _TO_BOOL_NONE_r11 1278 -#define _TO_BOOL_NONE_r22 1279 -#define _TO_BOOL_NONE_r33 1280 -#define _TO_BOOL_STR_r11 1281 -#define _TRACE_RECORD_r00 1282 -#define _UNARY_INVERT_r11 1283 -#define _UNARY_NEGATIVE_r11 1284 -#define _UNARY_NOT_r01 1285 -#define _UNARY_NOT_r11 1286 -#define _UNARY_NOT_r22 1287 -#define _UNARY_NOT_r33 1288 -#define _UNPACK_EX_r10 1289 -#define _UNPACK_SEQUENCE_r10 1290 -#define _UNPACK_SEQUENCE_LIST_r10 1291 -#define _UNPACK_SEQUENCE_TUPLE_r10 1292 -#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1293 -#define _WITH_EXCEPT_START_r33 1294 -#define _YIELD_VALUE_r11 1295 -#define MAX_UOP_REGS_ID 1295 +#define _FOR_ITER_GEN_FRAME_r03 731 +#define _FOR_ITER_GEN_FRAME_r13 732 +#define _FOR_ITER_GEN_FRAME_r23 733 +#define _FOR_ITER_TIER_TWO_r23 734 +#define _GET_AITER_r11 735 +#define _GET_ANEXT_r12 736 +#define _GET_AWAITABLE_r11 737 +#define _GET_ITER_r12 738 +#define _GET_LEN_r12 739 +#define _GET_YIELD_FROM_ITER_r11 740 +#define _GUARD_BINARY_OP_EXTEND_r22 741 +#define _GUARD_CALLABLE_ISINSTANCE_r03 742 +#define _GUARD_CALLABLE_ISINSTANCE_r13 743 +#define _GUARD_CALLABLE_ISINSTANCE_r23 744 +#define _GUARD_CALLABLE_ISINSTANCE_r33 745 +#define _GUARD_CALLABLE_LEN_r03 746 +#define _GUARD_CALLABLE_LEN_r13 747 +#define _GUARD_CALLABLE_LEN_r23 748 +#define _GUARD_CALLABLE_LEN_r33 749 +#define _GUARD_CALLABLE_LIST_APPEND_r03 750 +#define _GUARD_CALLABLE_LIST_APPEND_r13 751 +#define _GUARD_CALLABLE_LIST_APPEND_r23 752 +#define _GUARD_CALLABLE_LIST_APPEND_r33 753 +#define _GUARD_CALLABLE_STR_1_r03 754 +#define _GUARD_CALLABLE_STR_1_r13 755 +#define _GUARD_CALLABLE_STR_1_r23 756 +#define _GUARD_CALLABLE_STR_1_r33 757 +#define _GUARD_CALLABLE_TUPLE_1_r03 758 +#define _GUARD_CALLABLE_TUPLE_1_r13 759 +#define _GUARD_CALLABLE_TUPLE_1_r23 760 +#define _GUARD_CALLABLE_TUPLE_1_r33 761 +#define _GUARD_CALLABLE_TYPE_1_r03 762 +#define _GUARD_CALLABLE_TYPE_1_r13 763 +#define _GUARD_CALLABLE_TYPE_1_r23 764 +#define _GUARD_CALLABLE_TYPE_1_r33 765 +#define _GUARD_DORV_NO_DICT_r01 766 +#define _GUARD_DORV_NO_DICT_r11 767 +#define _GUARD_DORV_NO_DICT_r22 768 +#define _GUARD_DORV_NO_DICT_r33 769 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r01 770 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11 771 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22 772 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33 773 +#define _GUARD_GLOBALS_VERSION_r00 774 +#define _GUARD_GLOBALS_VERSION_r11 775 +#define _GUARD_GLOBALS_VERSION_r22 776 +#define _GUARD_GLOBALS_VERSION_r33 777 +#define _GUARD_IP_RETURN_GENERATOR_r00 778 +#define _GUARD_IP_RETURN_GENERATOR_r11 779 +#define _GUARD_IP_RETURN_GENERATOR_r22 780 +#define _GUARD_IP_RETURN_GENERATOR_r33 781 +#define _GUARD_IP_RETURN_VALUE_r00 782 +#define _GUARD_IP_RETURN_VALUE_r11 783 +#define _GUARD_IP_RETURN_VALUE_r22 784 +#define _GUARD_IP_RETURN_VALUE_r33 785 +#define _GUARD_IP_YIELD_VALUE_r00 786 +#define _GUARD_IP_YIELD_VALUE_r11 787 +#define _GUARD_IP_YIELD_VALUE_r22 788 +#define _GUARD_IP_YIELD_VALUE_r33 789 +#define _GUARD_IP__PUSH_FRAME_r00 790 +#define _GUARD_IP__PUSH_FRAME_r11 791 +#define _GUARD_IP__PUSH_FRAME_r22 792 +#define _GUARD_IP__PUSH_FRAME_r33 793 +#define _GUARD_IS_FALSE_POP_r00 794 +#define _GUARD_IS_FALSE_POP_r10 795 +#define _GUARD_IS_FALSE_POP_r21 796 +#define _GUARD_IS_FALSE_POP_r32 797 +#define _GUARD_IS_NONE_POP_r00 798 +#define _GUARD_IS_NONE_POP_r10 799 +#define _GUARD_IS_NONE_POP_r21 800 +#define _GUARD_IS_NONE_POP_r32 801 +#define _GUARD_IS_NOT_NONE_POP_r10 802 +#define _GUARD_IS_TRUE_POP_r00 803 +#define _GUARD_IS_TRUE_POP_r10 804 +#define _GUARD_IS_TRUE_POP_r21 805 +#define _GUARD_IS_TRUE_POP_r32 806 +#define _GUARD_KEYS_VERSION_r01 807 +#define _GUARD_KEYS_VERSION_r11 808 +#define _GUARD_KEYS_VERSION_r22 809 +#define _GUARD_KEYS_VERSION_r33 810 +#define _GUARD_NOS_DICT_r02 811 +#define _GUARD_NOS_DICT_r12 812 +#define _GUARD_NOS_DICT_r22 813 +#define _GUARD_NOS_DICT_r33 814 +#define _GUARD_NOS_FLOAT_r02 815 +#define _GUARD_NOS_FLOAT_r12 816 +#define _GUARD_NOS_FLOAT_r22 817 +#define _GUARD_NOS_FLOAT_r33 818 +#define _GUARD_NOS_INT_r02 819 +#define _GUARD_NOS_INT_r12 820 +#define _GUARD_NOS_INT_r22 821 +#define _GUARD_NOS_INT_r33 822 +#define _GUARD_NOS_LIST_r02 823 +#define _GUARD_NOS_LIST_r12 824 +#define _GUARD_NOS_LIST_r22 825 +#define _GUARD_NOS_LIST_r33 826 +#define _GUARD_NOS_NOT_NULL_r02 827 +#define _GUARD_NOS_NOT_NULL_r12 828 +#define _GUARD_NOS_NOT_NULL_r22 829 +#define _GUARD_NOS_NOT_NULL_r33 830 +#define _GUARD_NOS_NULL_r02 831 +#define _GUARD_NOS_NULL_r12 832 +#define _GUARD_NOS_NULL_r22 833 +#define _GUARD_NOS_NULL_r33 834 +#define _GUARD_NOS_OVERFLOWED_r02 835 +#define _GUARD_NOS_OVERFLOWED_r12 836 +#define _GUARD_NOS_OVERFLOWED_r22 837 +#define _GUARD_NOS_OVERFLOWED_r33 838 +#define _GUARD_NOS_TUPLE_r02 839 +#define _GUARD_NOS_TUPLE_r12 840 +#define _GUARD_NOS_TUPLE_r22 841 +#define _GUARD_NOS_TUPLE_r33 842 +#define _GUARD_NOS_UNICODE_r02 843 +#define _GUARD_NOS_UNICODE_r12 844 +#define _GUARD_NOS_UNICODE_r22 845 +#define _GUARD_NOS_UNICODE_r33 846 +#define _GUARD_NOT_EXHAUSTED_LIST_r02 847 +#define _GUARD_NOT_EXHAUSTED_LIST_r12 848 +#define _GUARD_NOT_EXHAUSTED_LIST_r22 849 +#define _GUARD_NOT_EXHAUSTED_LIST_r33 850 +#define _GUARD_NOT_EXHAUSTED_RANGE_r02 851 +#define _GUARD_NOT_EXHAUSTED_RANGE_r12 852 +#define _GUARD_NOT_EXHAUSTED_RANGE_r22 853 +#define _GUARD_NOT_EXHAUSTED_RANGE_r33 854 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r02 855 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r12 856 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r22 857 +#define _GUARD_NOT_EXHAUSTED_TUPLE_r33 858 +#define _GUARD_THIRD_NULL_r03 859 +#define _GUARD_THIRD_NULL_r13 860 +#define _GUARD_THIRD_NULL_r23 861 +#define _GUARD_THIRD_NULL_r33 862 +#define _GUARD_TOS_ANY_SET_r01 863 +#define _GUARD_TOS_ANY_SET_r11 864 +#define _GUARD_TOS_ANY_SET_r22 865 +#define _GUARD_TOS_ANY_SET_r33 866 +#define _GUARD_TOS_DICT_r01 867 +#define _GUARD_TOS_DICT_r11 868 +#define _GUARD_TOS_DICT_r22 869 +#define _GUARD_TOS_DICT_r33 870 +#define _GUARD_TOS_FLOAT_r01 871 +#define _GUARD_TOS_FLOAT_r11 872 +#define _GUARD_TOS_FLOAT_r22 873 +#define _GUARD_TOS_FLOAT_r33 874 +#define _GUARD_TOS_INT_r01 875 +#define _GUARD_TOS_INT_r11 876 +#define _GUARD_TOS_INT_r22 877 +#define _GUARD_TOS_INT_r33 878 +#define _GUARD_TOS_LIST_r01 879 +#define _GUARD_TOS_LIST_r11 880 +#define _GUARD_TOS_LIST_r22 881 +#define _GUARD_TOS_LIST_r33 882 +#define _GUARD_TOS_OVERFLOWED_r01 883 +#define _GUARD_TOS_OVERFLOWED_r11 884 +#define _GUARD_TOS_OVERFLOWED_r22 885 +#define _GUARD_TOS_OVERFLOWED_r33 886 +#define _GUARD_TOS_SLICE_r01 887 +#define _GUARD_TOS_SLICE_r11 888 +#define _GUARD_TOS_SLICE_r22 889 +#define _GUARD_TOS_SLICE_r33 890 +#define _GUARD_TOS_TUPLE_r01 891 +#define _GUARD_TOS_TUPLE_r11 892 +#define _GUARD_TOS_TUPLE_r22 893 +#define _GUARD_TOS_TUPLE_r33 894 +#define _GUARD_TOS_UNICODE_r01 895 +#define _GUARD_TOS_UNICODE_r11 896 +#define _GUARD_TOS_UNICODE_r22 897 +#define _GUARD_TOS_UNICODE_r33 898 +#define _GUARD_TYPE_VERSION_r01 899 +#define _GUARD_TYPE_VERSION_r11 900 +#define _GUARD_TYPE_VERSION_r22 901 +#define _GUARD_TYPE_VERSION_r33 902 +#define _GUARD_TYPE_VERSION_AND_LOCK_r01 903 +#define _GUARD_TYPE_VERSION_AND_LOCK_r11 904 +#define _GUARD_TYPE_VERSION_AND_LOCK_r22 905 +#define _GUARD_TYPE_VERSION_AND_LOCK_r33 906 +#define _HANDLE_PENDING_AND_DEOPT_r00 907 +#define _HANDLE_PENDING_AND_DEOPT_r10 908 +#define _HANDLE_PENDING_AND_DEOPT_r20 909 +#define _HANDLE_PENDING_AND_DEOPT_r30 910 +#define _IMPORT_FROM_r12 911 +#define _IMPORT_NAME_r21 912 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS_r00 913 +#define _INIT_CALL_PY_EXACT_ARGS_r01 914 +#define _INIT_CALL_PY_EXACT_ARGS_0_r01 915 +#define _INIT_CALL_PY_EXACT_ARGS_1_r01 916 +#define _INIT_CALL_PY_EXACT_ARGS_2_r01 917 +#define _INIT_CALL_PY_EXACT_ARGS_3_r01 918 +#define _INIT_CALL_PY_EXACT_ARGS_4_r01 919 +#define _INSERT_NULL_r10 920 +#define _INSTRUMENTED_FOR_ITER_r23 921 +#define _INSTRUMENTED_INSTRUCTION_r00 922 +#define _INSTRUMENTED_JUMP_FORWARD_r00 923 +#define _INSTRUMENTED_JUMP_FORWARD_r11 924 +#define _INSTRUMENTED_JUMP_FORWARD_r22 925 +#define _INSTRUMENTED_JUMP_FORWARD_r33 926 +#define _INSTRUMENTED_LINE_r00 927 +#define _INSTRUMENTED_NOT_TAKEN_r00 928 +#define _INSTRUMENTED_NOT_TAKEN_r11 929 +#define _INSTRUMENTED_NOT_TAKEN_r22 930 +#define _INSTRUMENTED_NOT_TAKEN_r33 931 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r00 932 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r10 933 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r21 934 +#define _INSTRUMENTED_POP_JUMP_IF_FALSE_r32 935 +#define _INSTRUMENTED_POP_JUMP_IF_NONE_r10 936 +#define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE_r10 937 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r00 938 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r10 939 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r21 940 +#define _INSTRUMENTED_POP_JUMP_IF_TRUE_r32 941 +#define _IS_NONE_r11 942 +#define _IS_OP_r21 943 +#define _ITER_CHECK_LIST_r02 944 +#define _ITER_CHECK_LIST_r12 945 +#define _ITER_CHECK_LIST_r22 946 +#define _ITER_CHECK_LIST_r33 947 +#define _ITER_CHECK_RANGE_r02 948 +#define _ITER_CHECK_RANGE_r12 949 +#define _ITER_CHECK_RANGE_r22 950 +#define _ITER_CHECK_RANGE_r33 951 +#define _ITER_CHECK_TUPLE_r02 952 +#define _ITER_CHECK_TUPLE_r12 953 +#define _ITER_CHECK_TUPLE_r22 954 +#define _ITER_CHECK_TUPLE_r33 955 +#define _ITER_JUMP_LIST_r02 956 +#define _ITER_JUMP_LIST_r12 957 +#define _ITER_JUMP_LIST_r22 958 +#define _ITER_JUMP_LIST_r33 959 +#define _ITER_JUMP_RANGE_r02 960 +#define _ITER_JUMP_RANGE_r12 961 +#define _ITER_JUMP_RANGE_r22 962 +#define _ITER_JUMP_RANGE_r33 963 +#define _ITER_JUMP_TUPLE_r02 964 +#define _ITER_JUMP_TUPLE_r12 965 +#define _ITER_JUMP_TUPLE_r22 966 +#define _ITER_JUMP_TUPLE_r33 967 +#define _ITER_NEXT_LIST_r23 968 +#define _ITER_NEXT_LIST_TIER_TWO_r23 969 +#define _ITER_NEXT_RANGE_r03 970 +#define _ITER_NEXT_RANGE_r13 971 +#define _ITER_NEXT_RANGE_r23 972 +#define _ITER_NEXT_TUPLE_r03 973 +#define _ITER_NEXT_TUPLE_r13 974 +#define _ITER_NEXT_TUPLE_r23 975 +#define _JUMP_BACKWARD_NO_INTERRUPT_r00 976 +#define _JUMP_BACKWARD_NO_INTERRUPT_r11 977 +#define _JUMP_BACKWARD_NO_INTERRUPT_r22 978 +#define _JUMP_BACKWARD_NO_INTERRUPT_r33 979 +#define _JUMP_TO_TOP_r00 980 +#define _LIST_APPEND_r10 981 +#define _LIST_EXTEND_r10 982 +#define _LOAD_ATTR_r10 983 +#define _LOAD_ATTR_CLASS_r11 984 +#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_r11 985 +#define _LOAD_ATTR_INSTANCE_VALUE_r02 986 +#define _LOAD_ATTR_INSTANCE_VALUE_r12 987 +#define _LOAD_ATTR_INSTANCE_VALUE_r23 988 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r02 989 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r12 990 +#define _LOAD_ATTR_METHOD_LAZY_DICT_r23 991 +#define _LOAD_ATTR_METHOD_NO_DICT_r02 992 +#define _LOAD_ATTR_METHOD_NO_DICT_r12 993 +#define _LOAD_ATTR_METHOD_NO_DICT_r23 994 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r02 995 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r12 996 +#define _LOAD_ATTR_METHOD_WITH_VALUES_r23 997 +#define _LOAD_ATTR_MODULE_r11 998 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11 999 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11 1000 +#define _LOAD_ATTR_PROPERTY_FRAME_r11 1001 +#define _LOAD_ATTR_SLOT_r11 1002 +#define _LOAD_ATTR_WITH_HINT_r11 1003 +#define _LOAD_BUILD_CLASS_r01 1004 +#define _LOAD_BYTECODE_r00 1005 +#define _LOAD_COMMON_CONSTANT_r01 1006 +#define _LOAD_COMMON_CONSTANT_r12 1007 +#define _LOAD_COMMON_CONSTANT_r23 1008 +#define _LOAD_CONST_r01 1009 +#define _LOAD_CONST_r12 1010 +#define _LOAD_CONST_r23 1011 +#define _LOAD_CONST_INLINE_r01 1012 +#define _LOAD_CONST_INLINE_r12 1013 +#define _LOAD_CONST_INLINE_r23 1014 +#define _LOAD_CONST_INLINE_BORROW_r01 1015 +#define _LOAD_CONST_INLINE_BORROW_r12 1016 +#define _LOAD_CONST_INLINE_BORROW_r23 1017 +#define _LOAD_CONST_UNDER_INLINE_r02 1018 +#define _LOAD_CONST_UNDER_INLINE_r12 1019 +#define _LOAD_CONST_UNDER_INLINE_r23 1020 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r02 1021 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r12 1022 +#define _LOAD_CONST_UNDER_INLINE_BORROW_r23 1023 +#define _LOAD_DEREF_r01 1024 +#define _LOAD_FAST_r01 1025 +#define _LOAD_FAST_r12 1026 +#define _LOAD_FAST_r23 1027 +#define _LOAD_FAST_0_r01 1028 +#define _LOAD_FAST_0_r12 1029 +#define _LOAD_FAST_0_r23 1030 +#define _LOAD_FAST_1_r01 1031 +#define _LOAD_FAST_1_r12 1032 +#define _LOAD_FAST_1_r23 1033 +#define _LOAD_FAST_2_r01 1034 +#define _LOAD_FAST_2_r12 1035 +#define _LOAD_FAST_2_r23 1036 +#define _LOAD_FAST_3_r01 1037 +#define _LOAD_FAST_3_r12 1038 +#define _LOAD_FAST_3_r23 1039 +#define _LOAD_FAST_4_r01 1040 +#define _LOAD_FAST_4_r12 1041 +#define _LOAD_FAST_4_r23 1042 +#define _LOAD_FAST_5_r01 1043 +#define _LOAD_FAST_5_r12 1044 +#define _LOAD_FAST_5_r23 1045 +#define _LOAD_FAST_6_r01 1046 +#define _LOAD_FAST_6_r12 1047 +#define _LOAD_FAST_6_r23 1048 +#define _LOAD_FAST_7_r01 1049 +#define _LOAD_FAST_7_r12 1050 +#define _LOAD_FAST_7_r23 1051 +#define _LOAD_FAST_AND_CLEAR_r01 1052 +#define _LOAD_FAST_AND_CLEAR_r12 1053 +#define _LOAD_FAST_AND_CLEAR_r23 1054 +#define _LOAD_FAST_BORROW_r01 1055 +#define _LOAD_FAST_BORROW_r12 1056 +#define _LOAD_FAST_BORROW_r23 1057 +#define _LOAD_FAST_BORROW_0_r01 1058 +#define _LOAD_FAST_BORROW_0_r12 1059 +#define _LOAD_FAST_BORROW_0_r23 1060 +#define _LOAD_FAST_BORROW_1_r01 1061 +#define _LOAD_FAST_BORROW_1_r12 1062 +#define _LOAD_FAST_BORROW_1_r23 1063 +#define _LOAD_FAST_BORROW_2_r01 1064 +#define _LOAD_FAST_BORROW_2_r12 1065 +#define _LOAD_FAST_BORROW_2_r23 1066 +#define _LOAD_FAST_BORROW_3_r01 1067 +#define _LOAD_FAST_BORROW_3_r12 1068 +#define _LOAD_FAST_BORROW_3_r23 1069 +#define _LOAD_FAST_BORROW_4_r01 1070 +#define _LOAD_FAST_BORROW_4_r12 1071 +#define _LOAD_FAST_BORROW_4_r23 1072 +#define _LOAD_FAST_BORROW_5_r01 1073 +#define _LOAD_FAST_BORROW_5_r12 1074 +#define _LOAD_FAST_BORROW_5_r23 1075 +#define _LOAD_FAST_BORROW_6_r01 1076 +#define _LOAD_FAST_BORROW_6_r12 1077 +#define _LOAD_FAST_BORROW_6_r23 1078 +#define _LOAD_FAST_BORROW_7_r01 1079 +#define _LOAD_FAST_BORROW_7_r12 1080 +#define _LOAD_FAST_BORROW_7_r23 1081 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r02 1082 +#define _LOAD_FAST_BORROW_LOAD_FAST_BORROW_r13 1083 +#define _LOAD_FAST_CHECK_r01 1084 +#define _LOAD_FAST_CHECK_r12 1085 +#define _LOAD_FAST_CHECK_r23 1086 +#define _LOAD_FAST_LOAD_FAST_r02 1087 +#define _LOAD_FAST_LOAD_FAST_r13 1088 +#define _LOAD_FROM_DICT_OR_DEREF_r11 1089 +#define _LOAD_FROM_DICT_OR_GLOBALS_r11 1090 +#define _LOAD_GLOBAL_r00 1091 +#define _LOAD_GLOBAL_BUILTINS_r01 1092 +#define _LOAD_GLOBAL_MODULE_r01 1093 +#define _LOAD_LOCALS_r01 1094 +#define _LOAD_LOCALS_r12 1095 +#define _LOAD_LOCALS_r23 1096 +#define _LOAD_NAME_r01 1097 +#define _LOAD_SMALL_INT_r01 1098 +#define _LOAD_SMALL_INT_r12 1099 +#define _LOAD_SMALL_INT_r23 1100 +#define _LOAD_SMALL_INT_0_r01 1101 +#define _LOAD_SMALL_INT_0_r12 1102 +#define _LOAD_SMALL_INT_0_r23 1103 +#define _LOAD_SMALL_INT_1_r01 1104 +#define _LOAD_SMALL_INT_1_r12 1105 +#define _LOAD_SMALL_INT_1_r23 1106 +#define _LOAD_SMALL_INT_2_r01 1107 +#define _LOAD_SMALL_INT_2_r12 1108 +#define _LOAD_SMALL_INT_2_r23 1109 +#define _LOAD_SMALL_INT_3_r01 1110 +#define _LOAD_SMALL_INT_3_r12 1111 +#define _LOAD_SMALL_INT_3_r23 1112 +#define _LOAD_SPECIAL_r00 1113 +#define _LOAD_SUPER_ATTR_ATTR_r31 1114 +#define _LOAD_SUPER_ATTR_METHOD_r32 1115 +#define _MAKE_CALLARGS_A_TUPLE_r33 1116 +#define _MAKE_CELL_r00 1117 +#define _MAKE_FUNCTION_r11 1118 +#define _MAKE_WARM_r00 1119 +#define _MAKE_WARM_r11 1120 +#define _MAKE_WARM_r22 1121 +#define _MAKE_WARM_r33 1122 +#define _MAP_ADD_r20 1123 +#define _MATCH_CLASS_r31 1124 +#define _MATCH_KEYS_r23 1125 +#define _MATCH_MAPPING_r02 1126 +#define _MATCH_MAPPING_r12 1127 +#define _MATCH_MAPPING_r23 1128 +#define _MATCH_SEQUENCE_r02 1129 +#define _MATCH_SEQUENCE_r12 1130 +#define _MATCH_SEQUENCE_r23 1131 +#define _MAYBE_EXPAND_METHOD_r00 1132 +#define _MAYBE_EXPAND_METHOD_KW_r11 1133 +#define _MONITOR_CALL_r00 1134 +#define _MONITOR_CALL_KW_r11 1135 +#define _MONITOR_JUMP_BACKWARD_r00 1136 +#define _MONITOR_JUMP_BACKWARD_r11 1137 +#define _MONITOR_JUMP_BACKWARD_r22 1138 +#define _MONITOR_JUMP_BACKWARD_r33 1139 +#define _MONITOR_RESUME_r00 1140 +#define _NOP_r00 1141 +#define _NOP_r11 1142 +#define _NOP_r22 1143 +#define _NOP_r33 1144 +#define _POP_CALL_r20 1145 +#define _POP_CALL_LOAD_CONST_INLINE_BORROW_r21 1146 +#define _POP_CALL_ONE_r30 1147 +#define _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW_r31 1148 +#define _POP_CALL_TWO_r30 1149 +#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW_r31 1150 +#define _POP_EXCEPT_r10 1151 +#define _POP_ITER_r20 1152 +#define _POP_JUMP_IF_FALSE_r00 1153 +#define _POP_JUMP_IF_FALSE_r10 1154 +#define _POP_JUMP_IF_FALSE_r21 1155 +#define _POP_JUMP_IF_FALSE_r32 1156 +#define _POP_JUMP_IF_TRUE_r00 1157 +#define _POP_JUMP_IF_TRUE_r10 1158 +#define _POP_JUMP_IF_TRUE_r21 1159 +#define _POP_JUMP_IF_TRUE_r32 1160 +#define _POP_TOP_r10 1161 +#define _POP_TOP_FLOAT_r00 1162 +#define _POP_TOP_FLOAT_r10 1163 +#define _POP_TOP_FLOAT_r21 1164 +#define _POP_TOP_FLOAT_r32 1165 +#define _POP_TOP_INT_r00 1166 +#define _POP_TOP_INT_r10 1167 +#define _POP_TOP_INT_r21 1168 +#define _POP_TOP_INT_r32 1169 +#define _POP_TOP_LOAD_CONST_INLINE_r11 1170 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW_r11 1171 +#define _POP_TOP_NOP_r00 1172 +#define _POP_TOP_NOP_r10 1173 +#define _POP_TOP_NOP_r21 1174 +#define _POP_TOP_NOP_r32 1175 +#define _POP_TOP_UNICODE_r00 1176 +#define _POP_TOP_UNICODE_r10 1177 +#define _POP_TOP_UNICODE_r21 1178 +#define _POP_TOP_UNICODE_r32 1179 +#define _POP_TWO_r20 1180 +#define _POP_TWO_LOAD_CONST_INLINE_BORROW_r21 1181 +#define _PUSH_EXC_INFO_r02 1182 +#define _PUSH_EXC_INFO_r12 1183 +#define _PUSH_EXC_INFO_r23 1184 +#define _PUSH_FRAME_r10 1185 +#define _PUSH_NULL_r01 1186 +#define _PUSH_NULL_r12 1187 +#define _PUSH_NULL_r23 1188 +#define _PUSH_NULL_CONDITIONAL_r00 1189 +#define _PY_FRAME_GENERAL_r01 1190 +#define _PY_FRAME_KW_r11 1191 +#define _QUICKEN_RESUME_r00 1192 +#define _QUICKEN_RESUME_r11 1193 +#define _QUICKEN_RESUME_r22 1194 +#define _QUICKEN_RESUME_r33 1195 +#define _REPLACE_WITH_TRUE_r11 1196 +#define _RESUME_CHECK_r00 1197 +#define _RESUME_CHECK_r11 1198 +#define _RESUME_CHECK_r22 1199 +#define _RESUME_CHECK_r33 1200 +#define _RETURN_GENERATOR_r01 1201 +#define _RETURN_VALUE_r11 1202 +#define _SAVE_RETURN_OFFSET_r00 1203 +#define _SAVE_RETURN_OFFSET_r11 1204 +#define _SAVE_RETURN_OFFSET_r22 1205 +#define _SAVE_RETURN_OFFSET_r33 1206 +#define _SEND_r22 1207 +#define _SEND_GEN_FRAME_r22 1208 +#define _SETUP_ANNOTATIONS_r00 1209 +#define _SET_ADD_r10 1210 +#define _SET_FUNCTION_ATTRIBUTE_r01 1211 +#define _SET_FUNCTION_ATTRIBUTE_r11 1212 +#define _SET_FUNCTION_ATTRIBUTE_r21 1213 +#define _SET_FUNCTION_ATTRIBUTE_r32 1214 +#define _SET_IP_r00 1215 +#define _SET_IP_r11 1216 +#define _SET_IP_r22 1217 +#define _SET_IP_r33 1218 +#define _SET_UPDATE_r10 1219 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r03 1220 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r13 1221 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r23 1222 +#define _SHUFFLE_3_LOAD_CONST_INLINE_BORROW_r33 1223 +#define _SPILL_OR_RELOAD_r01 1224 +#define _SPILL_OR_RELOAD_r02 1225 +#define _SPILL_OR_RELOAD_r03 1226 +#define _SPILL_OR_RELOAD_r10 1227 +#define _SPILL_OR_RELOAD_r12 1228 +#define _SPILL_OR_RELOAD_r13 1229 +#define _SPILL_OR_RELOAD_r20 1230 +#define _SPILL_OR_RELOAD_r21 1231 +#define _SPILL_OR_RELOAD_r23 1232 +#define _SPILL_OR_RELOAD_r30 1233 +#define _SPILL_OR_RELOAD_r31 1234 +#define _SPILL_OR_RELOAD_r32 1235 +#define _START_EXECUTOR_r00 1236 +#define _STORE_ATTR_r20 1237 +#define _STORE_ATTR_INSTANCE_VALUE_r21 1238 +#define _STORE_ATTR_SLOT_r21 1239 +#define _STORE_ATTR_WITH_HINT_r21 1240 +#define _STORE_DEREF_r10 1241 +#define _STORE_FAST_r10 1242 +#define _STORE_FAST_0_r10 1243 +#define _STORE_FAST_1_r10 1244 +#define _STORE_FAST_2_r10 1245 +#define _STORE_FAST_3_r10 1246 +#define _STORE_FAST_4_r10 1247 +#define _STORE_FAST_5_r10 1248 +#define _STORE_FAST_6_r10 1249 +#define _STORE_FAST_7_r10 1250 +#define _STORE_FAST_LOAD_FAST_r11 1251 +#define _STORE_FAST_STORE_FAST_r20 1252 +#define _STORE_GLOBAL_r10 1253 +#define _STORE_NAME_r10 1254 +#define _STORE_SLICE_r30 1255 +#define _STORE_SUBSCR_r30 1256 +#define _STORE_SUBSCR_DICT_r31 1257 +#define _STORE_SUBSCR_LIST_INT_r32 1258 +#define _SWAP_r11 1259 +#define _SWAP_2_r02 1260 +#define _SWAP_2_r12 1261 +#define _SWAP_2_r22 1262 +#define _SWAP_2_r33 1263 +#define _SWAP_3_r03 1264 +#define _SWAP_3_r13 1265 +#define _SWAP_3_r23 1266 +#define _SWAP_3_r33 1267 +#define _TIER2_RESUME_CHECK_r00 1268 +#define _TIER2_RESUME_CHECK_r11 1269 +#define _TIER2_RESUME_CHECK_r22 1270 +#define _TIER2_RESUME_CHECK_r33 1271 +#define _TO_BOOL_r11 1272 +#define _TO_BOOL_BOOL_r01 1273 +#define _TO_BOOL_BOOL_r11 1274 +#define _TO_BOOL_BOOL_r22 1275 +#define _TO_BOOL_BOOL_r33 1276 +#define _TO_BOOL_INT_r11 1277 +#define _TO_BOOL_LIST_r11 1278 +#define _TO_BOOL_NONE_r01 1279 +#define _TO_BOOL_NONE_r11 1280 +#define _TO_BOOL_NONE_r22 1281 +#define _TO_BOOL_NONE_r33 1282 +#define _TO_BOOL_STR_r11 1283 +#define _TRACE_RECORD_r00 1284 +#define _UNARY_INVERT_r11 1285 +#define _UNARY_NEGATIVE_r11 1286 +#define _UNARY_NOT_r01 1287 +#define _UNARY_NOT_r11 1288 +#define _UNARY_NOT_r22 1289 +#define _UNARY_NOT_r33 1290 +#define _UNPACK_EX_r10 1291 +#define _UNPACK_SEQUENCE_r10 1292 +#define _UNPACK_SEQUENCE_LIST_r10 1293 +#define _UNPACK_SEQUENCE_TUPLE_r10 1294 +#define _UNPACK_SEQUENCE_TWO_TUPLE_r12 1295 +#define _WITH_EXCEPT_START_r33 1296 +#define _YIELD_VALUE_r11 1297 +#define MAX_UOP_REGS_ID 1297 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 1281eeb041d..ec374dd5818 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -2131,10 +2131,10 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { }, }, [_FOR_ITER_GEN_FRAME] = { - .best = { 2, 2, 2, 2 }, + .best = { 0, 1, 2, 2 }, .entries = { - { -1, -1, -1 }, - { -1, -1, -1 }, + { 3, 0, _FOR_ITER_GEN_FRAME_r03 }, + { 3, 1, _FOR_ITER_GEN_FRAME_r13 }, { 3, 2, _FOR_ITER_GEN_FRAME_r23 }, { -1, -1, -1 }, }, @@ -3620,6 +3620,8 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_ITER_NEXT_RANGE_r03] = _ITER_NEXT_RANGE, [_ITER_NEXT_RANGE_r13] = _ITER_NEXT_RANGE, [_ITER_NEXT_RANGE_r23] = _ITER_NEXT_RANGE, + [_FOR_ITER_GEN_FRAME_r03] = _FOR_ITER_GEN_FRAME, + [_FOR_ITER_GEN_FRAME_r13] = _FOR_ITER_GEN_FRAME, [_FOR_ITER_GEN_FRAME_r23] = _FOR_ITER_GEN_FRAME, [_INSERT_NULL_r10] = _INSERT_NULL, [_LOAD_SPECIAL_r00] = _LOAD_SPECIAL, @@ -4182,6 +4184,8 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_FORMAT_WITH_SPEC] = "_FORMAT_WITH_SPEC", [_FORMAT_WITH_SPEC_r21] = "_FORMAT_WITH_SPEC_r21", [_FOR_ITER_GEN_FRAME] = "_FOR_ITER_GEN_FRAME", + [_FOR_ITER_GEN_FRAME_r03] = "_FOR_ITER_GEN_FRAME_r03", + [_FOR_ITER_GEN_FRAME_r13] = "_FOR_ITER_GEN_FRAME_r13", [_FOR_ITER_GEN_FRAME_r23] = "_FOR_ITER_GEN_FRAME_r23", [_FOR_ITER_TIER_TWO] = "_FOR_ITER_TIER_TWO", [_FOR_ITER_TIER_TWO_r23] = "_FOR_ITER_TIER_TWO_r23", diff --git a/Lib/test/test_free_threading/test_generators.py b/Lib/test/test_free_threading/test_generators.py index d01675eb38b..11f59301bcd 100644 --- a/Lib/test/test_free_threading/test_generators.py +++ b/Lib/test/test_free_threading/test_generators.py @@ -49,3 +49,74 @@ class TestFTGenerators(TestCase): self.concurrent_write_with_func(func=set_gen_name) with self.subTest(func=set_gen_qualname): self.concurrent_write_with_func(func=set_gen_qualname) + + def test_concurrent_send(self): + def gen(): + yield 1 + yield 2 + yield 3 + yield 4 + yield 5 + + def run_test(drive_generator): + g = gen() + values = [] + threading_helper.run_concurrently(drive_generator, self.NUM_THREADS, args=(g, values,)) + self.assertEqual(sorted(values), [1, 2, 3, 4, 5]) + + def call_next(g, values): + while True: + try: + values.append(next(g)) + except ValueError: + continue + except StopIteration: + break + + with self.subTest(method='next'): + run_test(call_next) + + def call_send(g, values): + while True: + try: + values.append(g.send(None)) + except ValueError: + continue + except StopIteration: + break + + with self.subTest(method='send'): + run_test(call_send) + + def for_iter_gen(g, values): + while True: + try: + for value in g: + values.append(value) + else: + break + except ValueError: + continue + + with self.subTest(method='for'): + run_test(for_iter_gen) + + def test_concurrent_close(self): + def gen(): + for i in range(10): + yield i + time.sleep(0.001) + + def drive_generator(g): + while True: + try: + for value in g: + if value == 5: + g.close() + else: + return + except ValueError as e: + self.assertEqual(e.args[0], "generator already executing") + + g = gen() + threading_helper.run_concurrently(drive_generator, self.NUM_THREADS, args=(g,)) diff --git a/Objects/genobject.c b/Objects/genobject.c index 3694198289d..508d215a0cf 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -36,6 +36,14 @@ static PyObject* async_gen_athrow_new(PyAsyncGenObject *, PyObject *); #define _PyAsyncGenObject_CAST(op) \ _Py_CAST(PyAsyncGenObject*, (op)) +#ifdef Py_GIL_DISABLED +# define _Py_GEN_TRY_SET_FRAME_STATE(gen, expected, state) \ + _Py_atomic_compare_exchange_int8(&(gen)->gi_frame_state, &expected, (state)) +#else +# define _Py_GEN_TRY_SET_FRAME_STATE(gen, expected, state) \ + ((gen)->gi_frame_state = (state), true) +#endif + static const char *NON_INIT_CORO_MSG = "can't send non-None value to a " "just-started coroutine"; @@ -145,10 +153,7 @@ _PyGen_Finalize(PyObject *self) static void gen_clear_frame(PyGenObject *gen) { - if (gen->gi_frame_state == FRAME_CLEARED) - return; - - gen->gi_frame_state = FRAME_CLEARED; + assert(gen->gi_frame_state == FRAME_CLEARED); _PyInterpreterFrame *frame = &gen->gi_iframe; frame->previous = NULL; _PyFrame_ClearExceptCode(frame); @@ -179,7 +184,10 @@ gen_dealloc(PyObject *self) if (PyCoro_CheckExact(gen)) { Py_CLEAR(((PyCoroObject *)gen)->cr_origin_or_finalizer); } - gen_clear_frame(gen); + if (gen->gi_frame_state != FRAME_CLEARED) { + gen->gi_frame_state = FRAME_CLEARED; + gen_clear_frame(gen); + } assert(gen->gi_exc_state.exc_value == NULL); PyStackRef_CLEAR(gen->gi_iframe.f_executable); Py_CLEAR(gen->gi_name); @@ -188,59 +196,32 @@ gen_dealloc(PyObject *self) PyObject_GC_Del(gen); } -static PySendResult -gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, - int exc, int closing) +static void +gen_raise_already_executing_error(PyGenObject *gen) { + const char *msg = "generator already executing"; + if (PyCoro_CheckExact(gen)) { + msg = "coroutine already executing"; + } + else if (PyAsyncGen_CheckExact(gen)) { + msg = "async generator already executing"; + } + PyErr_SetString(PyExc_ValueError, msg); +} + +// Send 'arg' into 'gen'. On success, return PYGEN_NEXT or PYGEN_RETURN. +// Returns PYGEN_ERROR on failure. 'presult' is set to the yielded or +// returned value. +// The generator must be in the FRAME_EXECUTING state when this function +// is called. +static PySendResult +gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc) +{ + assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_EXECUTING); + PyThreadState *tstate = _PyThreadState_GET(); _PyInterpreterFrame *frame = &gen->gi_iframe; - *presult = NULL; - if (gen->gi_frame_state == FRAME_CREATED && arg && arg != Py_None) { - const char *msg = "can't send non-None value to a " - "just-started generator"; - if (PyCoro_CheckExact(gen)) { - msg = NON_INIT_CORO_MSG; - } - else if (PyAsyncGen_CheckExact(gen)) { - msg = "can't send non-None value to a " - "just-started async generator"; - } - PyErr_SetString(PyExc_TypeError, msg); - return PYGEN_ERROR; - } - if (gen->gi_frame_state == FRAME_EXECUTING) { - const char *msg = "generator already executing"; - if (PyCoro_CheckExact(gen)) { - msg = "coroutine already executing"; - } - else if (PyAsyncGen_CheckExact(gen)) { - msg = "async generator already executing"; - } - PyErr_SetString(PyExc_ValueError, msg); - return PYGEN_ERROR; - } - if (FRAME_STATE_FINISHED(gen->gi_frame_state)) { - if (PyCoro_CheckExact(gen) && !closing) { - /* `gen` is an exhausted coroutine: raise an error, - except when called from gen_close(), which should - always be a silent method. */ - PyErr_SetString( - PyExc_RuntimeError, - "cannot reuse already awaited coroutine"); - } - else if (arg && !exc) { - /* `gen` is an exhausted generator: - only return value if called from send(). */ - *presult = Py_NewRef(Py_None); - return PYGEN_RETURN; - } - return PYGEN_ERROR; - } - - assert((gen->gi_frame_state == FRAME_CREATED) || - FRAME_STATE_SUSPENDED(gen->gi_frame_state)); - /* Push arg onto the frame's value stack */ PyObject *arg_obj = arg ? arg : Py_None; _PyFrame_StackPush(frame, PyStackRef_FromPyObjectNew(arg_obj)); @@ -254,21 +235,34 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(); } - gen->gi_frame_state = FRAME_EXECUTING; EVAL_CALL_STAT_INC(EVAL_CALL_GENERATOR); PyObject *result = _PyEval_EvalFrame(tstate, frame, exc); assert(tstate->exc_info == prev_exc_info); +#ifndef Py_GIL_DISABLED assert(gen->gi_exc_state.previous_item == NULL); - assert(gen->gi_frame_state != FRAME_EXECUTING); assert(frame->previous == NULL); + assert(gen->gi_frame_state != FRAME_EXECUTING); +#endif + + // The generator_return_kind field is used to distinguish between a + // yield and a return from within _PyEval_EvalFrame(). Earlier versions + // of CPython (prior to 3.15) used gi_frame_state for this purpose, but + // that requires the GIL for thread-safety. + int return_kind = ((_PyThreadStateImpl *)tstate)->generator_return_kind; + + if (return_kind == GENERATOR_YIELD) { + assert(result != NULL && !_PyErr_Occurred(tstate)); + *presult = result; + return PYGEN_NEXT; + } + + assert(return_kind == GENERATOR_RETURN); + assert(gen->gi_exc_state.exc_value == NULL); + assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_CLEARED); /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ if (result) { - if (FRAME_STATE_SUSPENDED(gen->gi_frame_state)) { - *presult = result; - return PYGEN_NEXT; - } assert(result == Py_None || !PyAsyncGen_CheckExact(gen)); if (result == Py_None && !PyAsyncGen_CheckExact(gen) && !arg) { /* Return NULL if called by gen_iternext() */ @@ -281,37 +275,82 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, !PyErr_ExceptionMatches(PyExc_StopAsyncIteration)); } - assert(gen->gi_exc_state.exc_value == NULL); - assert(gen->gi_frame_state == FRAME_CLEARED); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; } +// Set the generator 'gen' to the executing state and send 'arg' into it. +// See gen_send_ex2() for details. +static PySendResult +gen_send_ex(PyGenObject *gen, PyObject *arg, PyObject **presult) +{ + *presult = NULL; + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + do { + if (frame_state == FRAME_CREATED && arg && arg != Py_None) { + const char *msg = "can't send non-None value to a " + "just-started generator"; + if (PyCoro_CheckExact(gen)) { + msg = NON_INIT_CORO_MSG; + } + else if (PyAsyncGen_CheckExact(gen)) { + msg = "can't send non-None value to a " + "just-started async generator"; + } + PyErr_SetString(PyExc_TypeError, msg); + return PYGEN_ERROR; + } + if (frame_state == FRAME_EXECUTING) { + gen_raise_already_executing_error(gen); + return PYGEN_ERROR; + } + if (FRAME_STATE_FINISHED(frame_state)) { + if (PyCoro_CheckExact(gen)) { + /* `gen` is an exhausted coroutine: raise an error, + except when called from gen_close(), which should + always be a silent method. */ + PyErr_SetString( + PyExc_RuntimeError, + "cannot reuse already awaited coroutine"); + } + else if (arg) { + /* `gen` is an exhausted generator: + only return value if called from send(). */ + *presult = Py_None; + return PYGEN_RETURN; + } + return PYGEN_ERROR; + } + + assert((frame_state == FRAME_CREATED) || + FRAME_STATE_SUSPENDED(frame_state)); + } while (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_EXECUTING)); + + return gen_send_ex2(gen, arg, presult, 0); +} + static PySendResult PyGen_am_send(PyObject *self, PyObject *arg, PyObject **result) { PyGenObject *gen = _PyGen_CAST(self); - return gen_send_ex2(gen, arg, result, 0, 0); + return gen_send_ex(gen, arg, result); } static PyObject * -gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) +gen_set_stop_iteration(PyGenObject *gen, PyObject *result) { - PyObject *result; - if (gen_send_ex2(gen, arg, &result, exc, closing) == PYGEN_RETURN) { - if (PyAsyncGen_CheckExact(gen)) { - assert(result == Py_None); - PyErr_SetNone(PyExc_StopAsyncIteration); - } - else if (result == Py_None) { - PyErr_SetNone(PyExc_StopIteration); - } - else { - _PyGen_SetStopIterationValue(result); - } - Py_CLEAR(result); + if (PyAsyncGen_CheckExact(gen)) { + assert(result == Py_None); + PyErr_SetNone(PyExc_StopAsyncIteration); } - return result; + else if (result == Py_None) { + PyErr_SetNone(PyExc_StopIteration); + } + else { + _PyGen_SetStopIterationValue(result); + } + Py_DECREF(result); + return NULL; } PyDoc_STRVAR(send_doc, @@ -319,9 +358,14 @@ PyDoc_STRVAR(send_doc, return next yielded value or raise StopIteration."); static PyObject * -gen_send(PyObject *gen, PyObject *arg) +gen_send(PyObject *op, PyObject *arg) { - return gen_send_ex((PyGenObject*)gen, arg, 0, 0); + PyObject *result; + PyGenObject *gen = _PyGen_CAST(op); + if (gen_send_ex(gen, arg, &result) == PYGEN_RETURN) { + return gen_set_stop_iteration(gen, result); + } + return result; } PyDoc_STRVAR(close_doc, @@ -370,43 +414,43 @@ is_resume(_Py_CODEUNIT *instr) ); } -PyObject * -_PyGen_yf(PyGenObject *gen) -{ - if (gen->gi_frame_state == FRAME_SUSPENDED_YIELD_FROM) { - _PyInterpreterFrame *frame = &gen->gi_iframe; - // GH-122390: These asserts are wrong in the presence of ENTER_EXECUTOR! - // assert(is_resume(frame->instr_ptr)); - // assert((frame->instr_ptr->op.arg & RESUME_OPARG_LOCATION_MASK) >= RESUME_AFTER_YIELD_FROM); - return PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(frame)); - } - return NULL; -} - static PyObject * gen_close(PyObject *self, PyObject *args) { PyGenObject *gen = _PyGen_CAST(self); - if (gen->gi_frame_state == FRAME_CREATED) { - gen->gi_frame_state = FRAME_COMPLETED; - gen_clear_frame(gen); - Py_RETURN_NONE; - } - if (FRAME_STATE_FINISHED(gen->gi_frame_state)) { - Py_RETURN_NONE; - } + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + do { + if (frame_state == FRAME_CREATED) { + if (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_CLEARED)) { + continue; + } + gen_clear_frame(gen); + Py_RETURN_NONE; + } + + if (FRAME_STATE_FINISHED(frame_state)) { + Py_RETURN_NONE; + } + + if (frame_state == FRAME_EXECUTING) { + gen_raise_already_executing_error(gen); + return NULL; + } + + assert(frame_state == FRAME_SUSPENDED_YIELD_FROM || + frame_state == FRAME_SUSPENDED); + + } while (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_EXECUTING)); - PyObject *yf = _PyGen_yf(gen); int err = 0; - if (yf) { - PyFrameState state = gen->gi_frame_state; - gen->gi_frame_state = FRAME_EXECUTING; + _PyInterpreterFrame *frame = &gen->gi_iframe; + if (frame_state == FRAME_SUSPENDED_YIELD_FROM) { + PyObject *yf = PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(frame)); err = gen_close_iter(yf); - gen->gi_frame_state = state; Py_DECREF(yf); } - _PyInterpreterFrame *frame = &gen->gi_iframe; + if (is_resume(frame->instr_ptr)) { bool no_unwind_tools = _PyEval_NoToolsForUnwind(_PyThreadState_GET()); /* We can safely ignore the outermost try block @@ -416,7 +460,7 @@ gen_close(PyObject *self, PyObject *args) if (oparg & RESUME_OPARG_DEPTH1_MASK && no_unwind_tools) { // RESUME after YIELD_VALUE and exception depth is 1 assert((oparg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START); - gen->gi_frame_state = FRAME_COMPLETED; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_CLEARED); gen_clear_frame(gen); Py_RETURN_NONE; } @@ -425,8 +469,13 @@ gen_close(PyObject *self, PyObject *args) PyErr_SetNone(PyExc_GeneratorExit); } - PyObject *retval = gen_send_ex(gen, Py_None, 1, 1); - if (retval) { + PyObject *retval; + if (gen_send_ex2(gen, Py_None, &retval, 1) == PYGEN_RETURN) { + // the generator returned a value while closing, return the value here + assert(!PyErr_Occurred()); + return retval; + } + else if (retval) { const char *msg = "generator ignored GeneratorExit"; if (PyCoro_CheckExact(gen)) { msg = "coroutine ignored GeneratorExit"; @@ -443,102 +492,14 @@ gen_close(PyObject *self, PyObject *args) PyErr_Clear(); /* ignore this error */ Py_RETURN_NONE; } - - /* if the generator returned a value while closing, StopIteration was - * raised in gen_send_ex() above; retrieve and return the value here */ - if (_PyGen_FetchStopIterationValue(&retval) == 0) { - return retval; - } return NULL; } - -PyDoc_STRVAR(throw_doc, -"throw(value)\n\ -throw(type[,value[,tb]])\n\ -\n\ -Raise exception in generator, return next yielded value or raise\n\ -StopIteration.\n\ -the (type, val, tb) signature is deprecated, \n\ -and may be removed in a future version of Python."); - -static PyObject * -_gen_throw(PyGenObject *gen, int close_on_genexit, - PyObject *typ, PyObject *val, PyObject *tb) +// Set an exception for a gen.throw() call. +// Return 0 on success, -1 on failure. +static int +gen_set_exception(PyObject *typ, PyObject *val, PyObject *tb) { - PyObject *yf = _PyGen_yf(gen); - - if (yf) { - _PyInterpreterFrame *frame = &gen->gi_iframe; - PyObject *ret; - int err; - if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && - close_on_genexit - ) { - /* Asynchronous generators *should not* be closed right away. - We have to allow some awaits to work it through, hence the - `close_on_genexit` parameter here. - */ - PyFrameState state = gen->gi_frame_state; - gen->gi_frame_state = FRAME_EXECUTING; - err = gen_close_iter(yf); - gen->gi_frame_state = state; - Py_DECREF(yf); - if (err < 0) - return gen_send_ex(gen, Py_None, 1, 0); - goto throw_here; - } - PyThreadState *tstate = _PyThreadState_GET(); - assert(tstate != NULL); - if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { - /* `yf` is a generator or a coroutine. */ - - /* Link frame into the stack to enable complete backtraces. */ - /* XXX We should probably be updating the current frame somewhere in - ceval.c. */ - _PyInterpreterFrame *prev = tstate->current_frame; - frame->previous = prev; - tstate->current_frame = frame; - /* Close the generator that we are currently iterating with - 'yield from' or awaiting on with 'await'. */ - PyFrameState state = gen->gi_frame_state; - gen->gi_frame_state = FRAME_EXECUTING; - ret = _gen_throw((PyGenObject *)yf, close_on_genexit, - typ, val, tb); - gen->gi_frame_state = state; - tstate->current_frame = prev; - frame->previous = NULL; - } else { - /* `yf` is an iterator or a coroutine-like object. */ - PyObject *meth; - if (PyObject_GetOptionalAttr(yf, &_Py_ID(throw), &meth) < 0) { - Py_DECREF(yf); - return NULL; - } - if (meth == NULL) { - Py_DECREF(yf); - goto throw_here; - } - - _PyInterpreterFrame *prev = tstate->current_frame; - frame->previous = prev; - tstate->current_frame = frame; - PyFrameState state = gen->gi_frame_state; - gen->gi_frame_state = FRAME_EXECUTING; - ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); - gen->gi_frame_state = state; - tstate->current_frame = prev; - frame->previous = NULL; - Py_DECREF(meth); - } - Py_DECREF(yf); - if (!ret) { - ret = gen_send_ex(gen, Py_None, 1, 0); - } - return ret; - } - -throw_here: /* First, check the traceback argument, replacing None with NULL. */ if (tb == Py_None) { @@ -547,16 +508,16 @@ throw_here: else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "throw() third argument must be a traceback object"); - return NULL; + return -1; } Py_INCREF(typ); Py_XINCREF(val); Py_XINCREF(tb); - if (PyExceptionClass_Check(typ)) + if (PyExceptionClass_Check(typ)) { PyErr_NormalizeException(&typ, &val, &tb); - + } else if (PyExceptionInstance_Check(typ)) { /* Raising an instance. The value should be a dummy. */ if (val && val != Py_None) { @@ -584,14 +545,137 @@ throw_here: } PyErr_Restore(typ, val, tb); - return gen_send_ex(gen, Py_None, 1, 0); + return 0; failed_throw: /* Didn't use our arguments, so restore their original refcounts */ Py_DECREF(typ); Py_XDECREF(val); Py_XDECREF(tb); - return NULL; + return -1; +} + +static PyObject * +gen_throw_current_exception(PyGenObject *gen) +{ + assert(gen->gi_frame_state == FRAME_EXECUTING); + + PyObject *result; + if (gen_send_ex2(gen, Py_None, &result, 1) == PYGEN_RETURN) { + return gen_set_stop_iteration(gen, result); + } + return result; +} + +PyDoc_STRVAR(throw_doc, +"throw(value)\n\ +throw(type[,value[,tb]])\n\ +\n\ +Raise exception in generator, return next yielded value or raise\n\ +StopIteration.\n\ +the (type, val, tb) signature is deprecated, \n\ +and may be removed in a future version of Python."); + +static PyObject * +_gen_throw(PyGenObject *gen, int close_on_genexit, + PyObject *typ, PyObject *val, PyObject *tb) +{ + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + do { + if (frame_state == FRAME_EXECUTING) { + gen_raise_already_executing_error(gen); + return NULL; + } + + if (FRAME_STATE_FINISHED(frame_state)) { + if (PyCoro_CheckExact(gen)) { + /* `gen` is an exhausted coroutine: raise an error */ + PyErr_SetString( + PyExc_RuntimeError, + "cannot reuse already awaited coroutine"); + return NULL; + } + gen_set_exception(typ, val, tb); + return NULL; + } + + assert((frame_state == FRAME_CREATED) || + FRAME_STATE_SUSPENDED(frame_state)); + } while (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_EXECUTING)); + + if (frame_state == FRAME_SUSPENDED_YIELD_FROM) { + _PyInterpreterFrame *frame = &gen->gi_iframe; + PyObject *yf = PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(frame)); + PyObject *ret; + int err; + if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && + close_on_genexit + ) { + /* Asynchronous generators *should not* be closed right away. + We have to allow some awaits to work it through, hence the + `close_on_genexit` parameter here. + */ + err = gen_close_iter(yf); + Py_DECREF(yf); + if (err < 0) { + return gen_throw_current_exception(gen); + } + goto throw_here; + } + PyThreadState *tstate = _PyThreadState_GET(); + assert(tstate != NULL); + if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { + /* `yf` is a generator or a coroutine. */ + + /* Link frame into the stack to enable complete backtraces. */ + /* XXX We should probably be updating the current frame somewhere in + ceval.c. */ + _PyInterpreterFrame *prev = tstate->current_frame; + frame->previous = prev; + tstate->current_frame = frame; + /* Close the generator that we are currently iterating with + 'yield from' or awaiting on with 'await'. */ + ret = _gen_throw((PyGenObject *)yf, close_on_genexit, + typ, val, tb); + tstate->current_frame = prev; + frame->previous = NULL; + } + else { + /* `yf` is an iterator or a coroutine-like object. */ + PyObject *meth; + if (PyObject_GetOptionalAttr(yf, &_Py_ID(throw), &meth) < 0) { + Py_DECREF(yf); + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, frame_state); + return NULL; + } + if (meth == NULL) { + Py_DECREF(yf); + goto throw_here; + } + + _PyInterpreterFrame *prev = tstate->current_frame; + frame->previous = prev; + tstate->current_frame = frame; + ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); + tstate->current_frame = prev; + frame->previous = NULL; + Py_DECREF(meth); + } + Py_DECREF(yf); + if (!ret) { + return gen_throw_current_exception(gen); + } + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, frame_state); + return ret; + } + +throw_here: + assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_EXECUTING); + if (gen_set_exception(typ, val, tb) < 0) { + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, frame_state); + return NULL; + } + return gen_throw_current_exception(gen); } @@ -633,7 +717,7 @@ gen_iternext(PyObject *self) PyGenObject *gen = _PyGen_CAST(self); PyObject *result; - if (gen_send_ex2(gen, NULL, &result, 0, 0) == PYGEN_RETURN) { + if (gen_send_ex(gen, NULL, &result) == PYGEN_RETURN) { if (result != Py_None) { _PyGen_SetStopIterationValue(result); } @@ -757,13 +841,15 @@ gen_set_qualname(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) } static PyObject * -gen_getyieldfrom(PyObject *gen, void *Py_UNUSED(ignored)) +gen_getyieldfrom(PyObject *self, void *Py_UNUSED(ignored)) { - PyObject *yf = _PyGen_yf(_PyGen_CAST(gen)); - if (yf == NULL) { + PyGenObject *gen = _PyGen_CAST(self); + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + if (frame_state != FRAME_SUSPENDED_YIELD_FROM) { Py_RETURN_NONE; } - return yf; + // TODO: still not thread-safe with free threading + return PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(&gen->gi_iframe)); } @@ -771,17 +857,16 @@ static PyObject * gen_getrunning(PyObject *self, void *Py_UNUSED(ignored)) { PyGenObject *gen = _PyGen_CAST(self); - if (gen->gi_frame_state == FRAME_EXECUTING) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + return frame_state == FRAME_EXECUTING ? Py_True : Py_False; } static PyObject * gen_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) { PyGenObject *gen = _PyGen_CAST(self); - return PyBool_FromLong(FRAME_STATE_SUSPENDED(gen->gi_frame_state)); + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + return FRAME_STATE_SUSPENDED(frame_state) ? Py_True : Py_False; } static PyObject * @@ -790,9 +875,11 @@ _gen_getframe(PyGenObject *gen, const char *const name) if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) { return NULL; } - if (FRAME_STATE_FINISHED(gen->gi_frame_state)) { + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + if (FRAME_STATE_FINISHED(frame_state)) { Py_RETURN_NONE; } + // TODO: still not thread-safe with free threading return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(&gen->gi_iframe)); } @@ -1135,35 +1222,6 @@ coro_await(PyObject *coro) return (PyObject *)cw; } -static PyObject * -coro_get_cr_await(PyObject *coro, void *Py_UNUSED(ignored)) -{ - PyObject *yf = _PyGen_yf((PyGenObject *) coro); - if (yf == NULL) - Py_RETURN_NONE; - return yf; -} - -static PyObject * -cr_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) -{ - PyCoroObject *coro = _PyCoroObject_CAST(self); - if (FRAME_STATE_SUSPENDED(coro->cr_frame_state)) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; -} - -static PyObject * -cr_getrunning(PyObject *self, void *Py_UNUSED(ignored)) -{ - PyCoroObject *coro = _PyCoroObject_CAST(self); - if (coro->cr_frame_state == FRAME_EXECUTING) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; -} - static PyObject * cr_getframe(PyObject *coro, void *Py_UNUSED(ignored)) { @@ -1181,12 +1239,12 @@ static PyGetSetDef coro_getsetlist[] = { PyDoc_STR("name of the coroutine")}, {"__qualname__", gen_get_qualname, gen_set_qualname, PyDoc_STR("qualified name of the coroutine")}, - {"cr_await", coro_get_cr_await, NULL, + {"cr_await", gen_getyieldfrom, NULL, PyDoc_STR("object being awaited on, or None")}, - {"cr_running", cr_getrunning, NULL, NULL}, + {"cr_running", gen_getrunning, NULL, NULL}, {"cr_frame", cr_getframe, NULL, NULL}, {"cr_code", cr_getcode, NULL, NULL}, - {"cr_suspended", cr_getsuspended, NULL, NULL}, + {"cr_suspended", gen_getsuspended, NULL, NULL}, {NULL} /* Sentinel */ }; @@ -1602,26 +1660,16 @@ ag_getcode(PyObject *gen, void *Py_UNUSED(ignored)) return _gen_getcode((PyGenObject*)gen, "ag_code"); } -static PyObject * -ag_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) -{ - PyAsyncGenObject *ag = _PyAsyncGenObject_CAST(self); - if (FRAME_STATE_SUSPENDED(ag->ag_frame_state)) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; -} - static PyGetSetDef async_gen_getsetlist[] = { {"__name__", gen_get_name, gen_set_name, PyDoc_STR("name of the async generator")}, {"__qualname__", gen_get_qualname, gen_set_qualname, PyDoc_STR("qualified name of the async generator")}, - {"ag_await", coro_get_cr_await, NULL, + {"ag_await", gen_getyieldfrom, NULL, PyDoc_STR("object being awaited on, or None")}, {"ag_frame", ag_getframe, NULL, NULL}, {"ag_code", ag_getcode, NULL, NULL}, - {"ag_suspended", ag_getsuspended, NULL, NULL}, + {"ag_suspended", gen_getsuspended, NULL, NULL}, {NULL} /* Sentinel */ }; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 1291fe56a59..f7eb006e686 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1312,14 +1312,13 @@ dummy_func( assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); if ((tstate->interp->eval_frame == NULL) && (Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) && - ((PyGenObject *)receiver_o)->gi_frame_state < FRAME_EXECUTING) + gen_try_set_executing((PyGenObject *)receiver_o)) { PyGenObject *gen = (PyGenObject *)receiver_o; _PyInterpreterFrame *gen_frame = &gen->gi_iframe; _PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v)); DEAD(v); SYNC_SP(); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX); @@ -1360,12 +1359,11 @@ dummy_func( op(_SEND_GEN_FRAME, (receiver, v -- receiver, gen_frame)) { PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type); - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING); + DEOPT_IF(!gen_try_set_executing((PyGenObject *)gen)); STAT_INC(SEND, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v)); DEAD(v); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX); @@ -1389,7 +1387,6 @@ dummy_func( PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; _PyStackRef temp = retval; DEAD(retval); SAVE_STACK(); @@ -1399,6 +1396,8 @@ dummy_func( _PyInterpreterFrame *gen_frame = frame; frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); /* We don't know which of these is relevant here, so keep them equal */ assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE @@ -3405,18 +3404,10 @@ dummy_func( op(_FOR_ITER_GEN_FRAME, (iter, null -- iter, null, gen_frame)) { PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); DEOPT_IF(Py_TYPE(gen) != &PyGen_Type); -#ifdef Py_GIL_DISABLED - // Since generators can't be used by multiple threads anyway we - // don't need to deopt here, but this lets us work on making - // generators thread-safe without necessarily having to - // specialize them thread-safely as well. - DEOPT_IF(!_PyObject_IsUniquelyReferenced((PyObject *)gen)); -#endif - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING); + DEOPT_IF(!gen_try_set_executing((PyGenObject *)gen)); STAT_INC(FOR_ITER, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_None); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; pushed_frame->previous = frame; diff --git a/Python/ceval.c b/Python/ceval.c index cf86d5484f0..ec21d6bc2b8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2304,7 +2304,8 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) { assert(frame->owner == FRAME_OWNED_BY_GENERATOR); PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); - gen->gi_frame_state = FRAME_CLEARED; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_CLEARED); + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_RETURN; assert(tstate->exc_info == &gen->gi_exc_state); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; @@ -3979,15 +3980,13 @@ _PyEval_GetAwaitable(PyObject *iterable, int oparg) Py_TYPE(iterable), oparg); } else if (PyCoro_CheckExact(iter)) { - PyObject *yf = _PyGen_yf((PyGenObject*)iter); - if (yf != NULL) { - /* `iter` is a coroutine object that is being - awaited, `yf` is a pointer to the current awaitable - being awaited on. */ - Py_DECREF(yf); + PyCoroObject *coro = (PyCoroObject *)iter; + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(coro->cr_frame_state); + if (frame_state == FRAME_SUSPENDED_YIELD_FROM) { + /* `iter` is a coroutine object that is being awaited. */ Py_CLEAR(iter); _PyErr_SetString(PyThreadState_GET(), PyExc_RuntimeError, - "coroutine is being awaited already"); + "coroutine is being awaited already"); } } return iter; diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index a526a453dd9..c9472ec16d4 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -496,3 +496,28 @@ check_periodics(PyThreadState *tstate) { return 0; } +// Mark the generator as executing. Returns true if the state was changed, +// false if it was already executing or finished. +static inline bool +gen_try_set_executing(PyGenObject *gen) +{ +#ifdef Py_GIL_DISABLED + if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) { + int8_t frame_state = _Py_atomic_load_int8_relaxed(&gen->gi_frame_state); + while (frame_state < FRAME_EXECUTING) { + if (_Py_atomic_compare_exchange_int8(&gen->gi_frame_state, + &frame_state, + FRAME_EXECUTING)) { + return true; + } + } + } +#endif + // Use faster non-atomic modifications in the GIL-enabled build and when + // the object is uniquely referenced in the free-threaded build. + if (gen->gi_frame_state < FRAME_EXECUTING) { + gen->gi_frame_state = FRAME_EXECUTING; + return true; + } + return false; +} diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 2305df6ad5a..b3eff63b30a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -5823,7 +5823,7 @@ SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - if (gen->gi_frame_state >= FRAME_EXECUTING) { + if (!gen_try_set_executing((PyGenObject *)gen)) { UOP_STAT_INC(uopcode, miss); _tos_cache1 = v; _tos_cache0 = receiver; @@ -5833,7 +5833,6 @@ STAT_INC(SEND, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v)); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; assert( 2u + oparg <= UINT16_MAX); @@ -5861,7 +5860,6 @@ PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; _PyStackRef temp = retval; _PyFrame_SetStackPointer(frame, stack_pointer); tstate->exc_info = gen->gi_exc_state.previous_item; @@ -5870,6 +5868,8 @@ _PyInterpreterFrame *gen_frame = frame; frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE || @@ -10859,6 +10859,81 @@ break; } + case _FOR_ITER_GEN_FRAME_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + _PyStackRef iter; + _PyStackRef gen_frame; + oparg = CURRENT_OPARG(); + iter = stack_pointer[-2]; + PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(gen) != &PyGen_Type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (!gen_try_set_executing((PyGenObject *)gen)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(FOR_ITER, hit); + _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; + _PyFrame_StackPush(pushed_frame, PyStackRef_None); + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + pushed_frame->previous = frame; + frame->return_offset = (uint16_t)( 2u + oparg); + gen_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache2 = gen_frame; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + break; + } + + case _FOR_ITER_GEN_FRAME_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + _PyStackRef iter; + _PyStackRef gen_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + iter = stack_pointer[-1]; + PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(gen) != &PyGen_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (!gen_try_set_executing((PyGenObject *)gen)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(FOR_ITER, hit); + _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; + _PyFrame_StackPush(pushed_frame, PyStackRef_None); + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + pushed_frame->previous = frame; + frame->return_offset = (uint16_t)( 2u + oparg); + gen_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache2 = gen_frame; + _tos_cache1 = _stack_item_0; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); + break; + } + case _FOR_ITER_GEN_FRAME_r23: { CHECK_CURRENT_CACHED_VALUES(2); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); @@ -10876,17 +10951,7 @@ SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - #ifdef Py_GIL_DISABLED - - if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) { - UOP_STAT_INC(uopcode, miss); - _tos_cache1 = _stack_item_1; - _tos_cache0 = iter; - SET_CURRENT_CACHED_VALUES(2); - JUMP_TO_JUMP_TARGET(); - } - #endif - if (gen->gi_frame_state >= FRAME_EXECUTING) { + if (!gen_try_set_executing((PyGenObject *)gen)) { UOP_STAT_INC(uopcode, miss); _tos_cache1 = _stack_item_1; _tos_cache0 = iter; @@ -10896,7 +10961,6 @@ STAT_INC(FOR_ITER, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_None); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; pushed_frame->previous = frame; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 4482bb08a13..eaaa5f3bb96 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5548,14 +5548,7 @@ assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); JUMP_TO_PREDICTED(FOR_ITER); } - #ifdef Py_GIL_DISABLED - if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) { - UPDATE_MISS_STATS(FOR_ITER); - assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); - JUMP_TO_PREDICTED(FOR_ITER); - } - #endif - if (gen->gi_frame_state >= FRAME_EXECUTING) { + if (!gen_try_set_executing((PyGenObject *)gen)) { UPDATE_MISS_STATS(FOR_ITER); assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); JUMP_TO_PREDICTED(FOR_ITER); @@ -5563,7 +5556,6 @@ STAT_INC(FOR_ITER, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_None); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; pushed_frame->previous = frame; @@ -7327,7 +7319,6 @@ PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; _PyStackRef temp = retval; stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); @@ -7338,6 +7329,8 @@ _PyInterpreterFrame *gen_frame = frame; frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE || @@ -10301,14 +10294,13 @@ assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); if ((tstate->interp->eval_frame == NULL) && (Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) && - ((PyGenObject *)receiver_o)->gi_frame_state < FRAME_EXECUTING) + gen_try_set_executing((PyGenObject *)receiver_o)) { PyGenObject *gen = (PyGenObject *)receiver_o; _PyInterpreterFrame *gen_frame = &gen->gi_iframe; _PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v)); stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; assert( 2u + oparg <= UINT16_MAX); @@ -10401,7 +10393,7 @@ assert(_PyOpcode_Deopt[opcode] == (SEND)); JUMP_TO_PREDICTED(SEND); } - if (gen->gi_frame_state >= FRAME_EXECUTING) { + if (!gen_try_set_executing((PyGenObject *)gen)) { UPDATE_MISS_STATS(SEND); assert(_PyOpcode_Deopt[opcode] == (SEND)); JUMP_TO_PREDICTED(SEND); @@ -10409,7 +10401,6 @@ STAT_INC(SEND, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v)); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; assert( 2u + oparg <= UINT16_MAX); @@ -12045,7 +12036,6 @@ PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; _PyStackRef temp = retval; stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); @@ -12056,6 +12046,8 @@ _PyInterpreterFrame *gen_frame = frame; frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE || diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index fcd0dcf12ac..659befe312a 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -642,6 +642,7 @@ NON_ESCAPING_FUNCTIONS = ( "_PyFrame_StackPush", "_PyFunction_SetVersion", "_PyGen_GetGeneratorFromFrame", + "gen_try_set_executing", "_PyInterpreterState_GET", "_PyList_AppendTakeRef", "_PyList_ITEMS", From 4ea3c1a04747978361b497798428423cbb6a7146 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 19 Dec 2025 17:33:49 -0500 Subject: [PATCH 55/80] gh-120321: Fix TSan reported race in gen_clear_frame (gh-142995) TSan treats compare-exchanges that fail as if they are writes so there is a false positive with the read of gi_frame_state in gen_close. --- Objects/genobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/genobject.c b/Objects/genobject.c index 508d215a0cf..1e59d89f5ce 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -153,7 +153,7 @@ _PyGen_Finalize(PyObject *self) static void gen_clear_frame(PyGenObject *gen) { - assert(gen->gi_frame_state == FRAME_CLEARED); + assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_CLEARED); _PyInterpreterFrame *frame = &gen->gi_iframe; frame->previous = NULL; _PyFrame_ClearExceptCode(frame); From e46f28c6afce9c85e4bc4a113d1c7efc472e7d8f Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 19 Dec 2025 18:06:47 -0500 Subject: [PATCH 56/80] gh-129069: Fix listobject.c data races due to memmove (gh-142957) The use of memmove and _Py_memory_repeat were not thread-safe in the free threading build in some cases. In theory, memmove and _Py_memory_repeat can copy byte-by-byte instead of pointer-by-pointer, so concurrent readers could see uninitialized data or tearing. Additionally, we should be using "release" (or stronger) ordering to be compliant with the C11 memory model when copying objects within a list. --- Objects/listobject.c | 103 +++++++++++++-------- Tools/tsan/suppressions_free_threading.txt | 6 -- 2 files changed, 62 insertions(+), 47 deletions(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index 1722ea60cdc..f67d1a45a49 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -96,11 +96,7 @@ ensure_shared_on_resize(PyListObject *self) * of the new slots at exit is undefined heap trash; it's the caller's * responsibility to overwrite them with sane values. * The number of allocated elements may grow, shrink, or stay the same. - * Failure is impossible if newsize <= self.allocated on entry, although - * that partly relies on an assumption that the system realloc() never - * fails when passed a number of bytes <= the number of bytes last - * allocated (the C standard doesn't guarantee this, but it's hard to - * imagine a realloc implementation where it wouldn't be true). + * Failure is impossible if newsize <= self.allocated on entry. * Note that self->ob_item may change, and even if newsize is less * than ob_size on entry. */ @@ -145,6 +141,11 @@ list_resize(PyListObject *self, Py_ssize_t newsize) #ifdef Py_GIL_DISABLED _PyListArray *array = list_allocate_array(new_allocated); if (array == NULL) { + if (newsize < allocated) { + // Never fail when shrinking allocations + Py_SET_SIZE(self, newsize); + return 0; + } PyErr_NoMemory(); return -1; } @@ -178,6 +179,11 @@ list_resize(PyListObject *self, Py_ssize_t newsize) items = NULL; } if (items == NULL) { + if (newsize < allocated) { + // Never fail when shrinking allocations + Py_SET_SIZE(self, newsize); + return 0; + } PyErr_NoMemory(); return -1; } @@ -818,8 +824,8 @@ list_repeat_lock_held(PyListObject *a, Py_ssize_t n) _Py_RefcntAdd(*src, n); *dest++ = *src++; } - // TODO: _Py_memory_repeat calls are not safe for shared lists in - // GIL_DISABLED builds. (See issue #129069) + // This list is not yet visible to other threads, so atomic repeat + // is not necessary even in Py_GIL_DISABLED builds. _Py_memory_repeat((char *)np->ob_item, sizeof(PyObject *)*output_size, sizeof(PyObject *)*input_size); } @@ -882,6 +888,34 @@ list_clear_slot(PyObject *self) return 0; } +// Pointer-by-pointer memmove for PyObject** arrays that is safe +// for shared lists in Py_GIL_DISABLED builds. +static void +ptr_wise_atomic_memmove(PyListObject *a, PyObject **dest, PyObject **src, Py_ssize_t n) +{ +#ifndef Py_GIL_DISABLED + memmove(dest, src, n * sizeof(PyObject *)); +#else + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(a); + if (_Py_IsOwnedByCurrentThread((PyObject *)a) && !_PyObject_GC_IS_SHARED(a)) { + // No other threads can read this list concurrently + memmove(dest, src, n * sizeof(PyObject *)); + return; + } + if (dest < src) { + for (Py_ssize_t i = 0; i != n; i++) { + _Py_atomic_store_ptr_release(&dest[i], src[i]); + } + } + else { + // copy backwards to avoid overwriting src before it's read + for (Py_ssize_t i = n; i != 0; i--) { + _Py_atomic_store_ptr_release(&dest[i - 1], src[i - 1]); + } + } +#endif +} + /* a[ilow:ihigh] = v if v != NULL. * del a[ilow:ihigh] if v == NULL. * @@ -952,16 +986,9 @@ list_ass_slice_lock_held(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyO } if (d < 0) { /* Delete -d items */ - Py_ssize_t tail; - tail = (Py_SIZE(a) - ihigh) * sizeof(PyObject *); - // TODO: these memmove/memcpy calls are not safe for shared lists in - // GIL_DISABLED builds. (See issue #129069) - memmove(&item[ihigh+d], &item[ihigh], tail); - if (list_resize(a, Py_SIZE(a) + d) < 0) { - memmove(&item[ihigh], &item[ihigh+d], tail); - memcpy(&item[ilow], recycle, s); - goto Error; - } + Py_ssize_t tail = Py_SIZE(a) - ihigh; + ptr_wise_atomic_memmove(a, &item[ihigh+d], &item[ihigh], tail); + (void)list_resize(a, Py_SIZE(a) + d); // NB: shrinking a list can't fail item = a->ob_item; } else if (d > 0) { /* Insert d items */ @@ -969,10 +996,7 @@ list_ass_slice_lock_held(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyO if (list_resize(a, k+d) < 0) goto Error; item = a->ob_item; - // TODO: these memmove/memcpy calls are not safe for shared lists in - // GIL_DISABLED builds. (See issue #129069) - memmove(&item[ihigh+d], &item[ihigh], - (k - ihigh)*sizeof(PyObject *)); + ptr_wise_atomic_memmove(a, &item[ihigh+d], &item[ihigh], k - ihigh); } for (k = 0; k < n; k++, ilow++) { PyObject *w = vitem[k]; @@ -1056,10 +1080,17 @@ list_inplace_repeat_lock_held(PyListObject *self, Py_ssize_t n) for (Py_ssize_t j = 0; j < input_size; j++) { _Py_RefcntAdd(items[j], n-1); } - // TODO: _Py_memory_repeat calls are not safe for shared lists in - // GIL_DISABLED builds. (See issue #129069) +#ifndef Py_GIL_DISABLED _Py_memory_repeat((char *)items, sizeof(PyObject *)*output_size, sizeof(PyObject *)*input_size); +#else + Py_ssize_t copied = input_size; + while (copied < output_size) { + Py_ssize_t items_to_copy = Py_MIN(copied, output_size - copied); + ptr_wise_atomic_memmove(self, items + copied, items, items_to_copy); + copied += items_to_copy; + } +#endif return 0; } @@ -1532,7 +1563,6 @@ list_pop_impl(PyListObject *self, Py_ssize_t index) /*[clinic end generated code: output=6bd69dcb3f17eca8 input=c269141068ae4b8f]*/ { PyObject *v; - int status; if (Py_SIZE(self) == 0) { /* Special-case most common failure cause */ @@ -1548,27 +1578,18 @@ list_pop_impl(PyListObject *self, Py_ssize_t index) PyObject **items = self->ob_item; v = items[index]; - const Py_ssize_t size_after_pop = Py_SIZE(self) - 1; - if (size_after_pop == 0) { + if (Py_SIZE(self) == 1) { Py_INCREF(v); list_clear(self); - status = 0; + return v; } - else { - if ((size_after_pop - index) > 0) { - memmove(&items[index], &items[index+1], (size_after_pop - index) * sizeof(PyObject *)); - } - status = list_resize(self, size_after_pop); - } - if (status >= 0) { - return v; // and v now owns the reference the list had - } - else { - // list resize failed, need to restore - memmove(&items[index+1], &items[index], (size_after_pop - index)* sizeof(PyObject *)); - items[index] = v; - return NULL; + Py_ssize_t size_after_pop = Py_SIZE(self) - 1; + if (index < size_after_pop) { + ptr_wise_atomic_memmove(self, &items[index], &items[index+1], + size_after_pop - index); } + list_resize(self, size_after_pop); // NB: shrinking a list can't fail + return v; } /* Reverse a slice of a list in place, from lo up to (exclusive) hi. */ diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index adc85d631db..e8b1501c34b 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -23,12 +23,6 @@ race_top:write_thread_id # https://gist.github.com/mpage/6962e8870606cfc960e159b407a0cb40 thread:pthread_create -# List resizing happens through different paths ending in memcpy or memmove -# (for efficiency), which will probably need to rewritten as explicit loops -# of ptr-sized copies to be thread-safe. (Issue #129069) -race:list_ass_slice_lock_held -race:list_inplace_repeat_lock_held - # PyObject_Realloc internally does memcpy which isn't atomic so can race # with non-locking reads. See #132070 race:PyObject_Realloc From 5b5263648f2404b22f9a596c67350c2e602df52b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sat, 20 Dec 2025 03:36:09 +0200 Subject: [PATCH 57/80] gh-142927: Tachyon: Start with user's default light/dark theme (#142987) --- .../sampling/_heatmap_assets/heatmap.js | 26 +------------- .../sampling/_heatmap_assets/heatmap_index.js | 27 +------------- .../heatmap_index_template.html | 2 +- .../heatmap_pyfile_template.html | 2 +- .../_heatmap_assets/heatmap_shared.js | 36 +++++++++++++++++++ 5 files changed, 40 insertions(+), 53 deletions(-) diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap.js b/Lib/profiling/sampling/_heatmap_assets/heatmap.js index d6a91ef2903..90b5b111d36 100644 --- a/Lib/profiling/sampling/_heatmap_assets/heatmap.js +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap.js @@ -15,37 +15,13 @@ let coldCodeHidden = false; // ============================================================================ function toggleTheme() { - const html = document.documentElement; - const current = html.getAttribute('data-theme') || 'light'; - const next = current === 'light' ? 'dark' : 'light'; - html.setAttribute('data-theme', next); - localStorage.setItem('heatmap-theme', next); - - // Update theme button icon - const btn = document.getElementById('theme-btn'); - if (btn) { - btn.querySelector('.icon-moon').style.display = next === 'dark' ? 'none' : ''; - btn.querySelector('.icon-sun').style.display = next === 'dark' ? '' : 'none'; - } + toggleAndSaveTheme(); applyLineColors(); // Rebuild scroll marker with new theme colors buildScrollMarker(); } -function restoreUIState() { - // Restore theme - const savedTheme = localStorage.getItem('heatmap-theme'); - if (savedTheme) { - document.documentElement.setAttribute('data-theme', savedTheme); - const btn = document.getElementById('theme-btn'); - if (btn) { - btn.querySelector('.icon-moon').style.display = savedTheme === 'dark' ? 'none' : ''; - btn.querySelector('.icon-sun').style.display = savedTheme === 'dark' ? '' : 'none'; - } - } -} - // ============================================================================ // Utility Functions // ============================================================================ diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js b/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js index 8eb6af0db53..db4b5485056 100644 --- a/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js @@ -19,35 +19,10 @@ function applyHeatmapBarColors() { // ============================================================================ function toggleTheme() { - const html = document.documentElement; - const current = html.getAttribute('data-theme') || 'light'; - const next = current === 'light' ? 'dark' : 'light'; - html.setAttribute('data-theme', next); - localStorage.setItem('heatmap-theme', next); - - // Update theme button icon - const btn = document.getElementById('theme-btn'); - if (btn) { - btn.querySelector('.icon-moon').style.display = next === 'dark' ? 'none' : ''; - btn.querySelector('.icon-sun').style.display = next === 'dark' ? '' : 'none'; - } - + toggleAndSaveTheme(); applyHeatmapBarColors(); } -function restoreUIState() { - // Restore theme - const savedTheme = localStorage.getItem('heatmap-theme'); - if (savedTheme) { - document.documentElement.setAttribute('data-theme', savedTheme); - const btn = document.getElementById('theme-btn'); - if (btn) { - btn.querySelector('.icon-moon').style.display = savedTheme === 'dark' ? 'none' : ''; - btn.querySelector('.icon-sun').style.display = savedTheme === 'dark' ? '' : 'none'; - } - } -} - // ============================================================================ // Type Section Toggle (stdlib, project, etc) // ============================================================================ diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html b/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html index 3620f8efb80..8d04149abe3 100644 --- a/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html @@ -1,5 +1,5 @@ - + diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html b/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html index 91b629b2628..2a9c07647e6 100644 --- a/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html @@ -1,5 +1,5 @@ - + diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_shared.js b/Lib/profiling/sampling/_heatmap_assets/heatmap_shared.js index 7fcd720d45d..84b13ca0a96 100644 --- a/Lib/profiling/sampling/_heatmap_assets/heatmap_shared.js +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_shared.js @@ -39,6 +39,42 @@ function intensityToColor(intensity) { return rootStyle.getPropertyValue(`--heat-${level}`).trim(); } +// ============================================================================ +// Theme Support +// ============================================================================ + +// Get the preferred theme from localStorage or browser preference +function getPreferredTheme() { + const saved = localStorage.getItem('heatmap-theme'); + if (saved) return saved; + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; +} + +// Apply theme and update UI. Returns the applied theme. +function applyTheme(theme) { + document.documentElement.setAttribute('data-theme', theme); + const btn = document.getElementById('theme-btn'); + if (btn) { + btn.querySelector('.icon-moon').style.display = theme === 'dark' ? 'none' : ''; + btn.querySelector('.icon-sun').style.display = theme === 'dark' ? '' : 'none'; + } + return theme; +} + +// Toggle theme and save preference. Returns the new theme. +function toggleAndSaveTheme() { + const current = document.documentElement.getAttribute('data-theme') || 'light'; + const next = current === 'light' ? 'dark' : 'light'; + applyTheme(next); + localStorage.setItem('heatmap-theme', next); + return next; +} + +// Restore theme from localStorage, or use browser preference +function restoreUIState() { + applyTheme(getPreferredTheme()); +} + // ============================================================================ // Favicon (Reuse logo image as favicon) // ============================================================================ From 5989095dfd08735525f2b615066bc3c231b09388 Mon Sep 17 00:00:00 2001 From: AZero13 Date: Sat, 20 Dec 2025 02:37:10 -0500 Subject: [PATCH 58/80] gh-143012: use `Py_ssize_t` cast for `PyBytes_FromStringAndSize` (#143013) --- Modules/posixmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 221cfc5a934..e0276ce9e39 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1815,7 +1815,7 @@ convertenviron(void) #ifdef MS_WINDOWS k = PyUnicode_FromWideChar(*e, (Py_ssize_t)(p-*e)); #else - k = PyBytes_FromStringAndSize(*e, (int)(p-*e)); + k = PyBytes_FromStringAndSize(*e, (Py_ssize_t)(p-*e)); #endif if (k == NULL) { Py_DECREF(d); From 3cc57505e530c64b7d783e5ac15d3e66d233bbbb Mon Sep 17 00:00:00 2001 From: Hai Zhu <35182391+cocolato@users.noreply.github.com> Date: Sun, 21 Dec 2025 01:27:34 +0800 Subject: [PATCH 59/80] gh-142834: pdb commands command should use last available breakpoint (#142835) --- Doc/library/pdb.rst | 3 +- Lib/pdb.py | 9 +++- Lib/test/test_pdb.py | 43 +++++++++++++++++++ ...-12-16-15-32-41.gh-issue-142834.g7mHw_.rst | 1 + 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-16-15-32-41.gh-issue-142834.g7mHw_.rst diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index 0bbdc425352..8ab3e7ec9ef 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -520,7 +520,8 @@ can be overridden by the local file. To remove all commands from a breakpoint, type ``commands`` and follow it immediately with ``end``; that is, give no commands. - With no *bpnumber* argument, ``commands`` refers to the last breakpoint set. + With no *bpnumber* argument, ``commands`` refers to the most recently set + breakpoint that still exists. You can use breakpoint commands to start your program up again. Simply use the :pdbcmd:`continue` command, or :pdbcmd:`step`, diff --git a/Lib/pdb.py b/Lib/pdb.py index c1a5db080dc..4a6bc17e91c 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -1315,7 +1315,14 @@ class Pdb(bdb.Bdb, cmd.Cmd): reached. """ if not arg: - bnum = len(bdb.Breakpoint.bpbynumber) - 1 + for bp in reversed(bdb.Breakpoint.bpbynumber): + if bp is None: + continue + bnum = bp.number + break + else: + self.error('cannot set commands: no existing breakpoint') + return else: try: bnum = int(arg) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 5cba34ff8ba..4352aa6abfe 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3478,6 +3478,49 @@ def test_pdb_issue_gh_65052(): (Pdb) continue """ +def test_pdb_commands_last_breakpoint(): + """See GH-142834 + + >>> def test_function(): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... foo = 1 + ... bar = 2 + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE + ... 'break 4', + ... 'break 3', + ... 'clear 2', + ... 'commands', + ... 'p "success"', + ... 'end', + ... 'continue', + ... 'clear 1', + ... 'commands', + ... 'continue', + ... ]): + ... test_function() + > (2)test_function() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) break 4 + Breakpoint 1 at :4 + (Pdb) break 3 + Breakpoint 2 at :3 + (Pdb) clear 2 + Deleted breakpoint 2 at :3 + (Pdb) commands + (com) p "success" + (com) end + (Pdb) continue + 'success' + > (4)test_function() + -> bar = 2 + (Pdb) clear 1 + Deleted breakpoint 1 at :4 + (Pdb) commands + *** cannot set commands: no existing breakpoint + (Pdb) continue + """ + @support.force_not_colorized_test_class @support.requires_subprocess() diff --git a/Misc/NEWS.d/next/Library/2025-12-16-15-32-41.gh-issue-142834.g7mHw_.rst b/Misc/NEWS.d/next/Library/2025-12-16-15-32-41.gh-issue-142834.g7mHw_.rst new file mode 100644 index 00000000000..8cde592e7c9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-16-15-32-41.gh-issue-142834.g7mHw_.rst @@ -0,0 +1 @@ +Change the :mod:`pdb` ``commands`` command to use the last available breakpoint instead of failing when the most recently created breakpoint was deleted. From 7607712b61b7ab35c08e189155c0e161f9ad74b0 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Sat, 20 Dec 2025 14:42:12 -0500 Subject: [PATCH 60/80] gh-120321: Avoid `-Wunreachable-code` warning on Clang (gh-143022) --- Objects/genobject.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Objects/genobject.c b/Objects/genobject.c index 1e59d89f5ce..020af903a3f 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -422,7 +422,8 @@ gen_close(PyObject *self, PyObject *args) int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); do { if (frame_state == FRAME_CREATED) { - if (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_CLEARED)) { + // && (1) to avoid -Wunreachable-code warning on Clang + if (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_CLEARED) && (1)) { continue; } gen_clear_frame(gen); From 2b4feee648b7af0ccca8dee167fdd21cfb0bd23a Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Sat, 20 Dec 2025 15:37:31 -0500 Subject: [PATCH 61/80] gh-122581: Use parser mutex in default build for subinterpreters (gh-142959) --- Include/internal/pycore_parser.h | 16 ---------------- Include/internal/pycore_runtime_structs.h | 2 -- Parser/pegen.c | 19 +++++++++---------- 3 files changed, 9 insertions(+), 28 deletions(-) diff --git a/Include/internal/pycore_parser.h b/Include/internal/pycore_parser.h index 2c46f59ab7d..b89d02035db 100644 --- a/Include/internal/pycore_parser.h +++ b/Include/internal/pycore_parser.h @@ -14,21 +14,6 @@ extern "C" { #include "pycore_pyarena.h" // PyArena _Py_DECLARE_STR(empty, "") -#if defined(Py_DEBUG) && defined(Py_GIL_DISABLED) -#define _parser_runtime_state_INIT \ - { \ - .mutex = {0}, \ - .dummy_name = { \ - .kind = Name_kind, \ - .v.Name.id = &_Py_STR(empty), \ - .v.Name.ctx = Load, \ - .lineno = 1, \ - .col_offset = 0, \ - .end_lineno = 1, \ - .end_col_offset = 0, \ - }, \ - } -#else #define _parser_runtime_state_INIT \ { \ .dummy_name = { \ @@ -41,7 +26,6 @@ _Py_DECLARE_STR(empty, "") .end_col_offset = 0, \ }, \ } -#endif extern struct _mod* _PyParser_ASTFromString( const char *str, diff --git a/Include/internal/pycore_runtime_structs.h b/Include/internal/pycore_runtime_structs.h index 995f49e78dc..92387031ad7 100644 --- a/Include/internal/pycore_runtime_structs.h +++ b/Include/internal/pycore_runtime_structs.h @@ -77,9 +77,7 @@ struct _fileutils_state { struct _parser_runtime_state { #ifdef Py_DEBUG long memo_statistics[_PYPEGEN_NSTATISTICS]; -#ifdef Py_GIL_DISABLED PyMutex mutex; -#endif #else int _not_used; #endif diff --git a/Parser/pegen.c b/Parser/pegen.c index a38e973b3f6..7ecc55eee13 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -3,9 +3,8 @@ #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_parser.h" // _PYPEGEN_NSTATISTICS #include "pycore_pyerrors.h" // PyExc_IncompleteInputError -#include "pycore_runtime.h" // _PyRuntime +#include "pycore_runtime.h" // _PyRuntime #include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal -#include "pycore_pyatomic_ft_wrappers.h" #include #include "lexer/lexer.h" @@ -303,11 +302,11 @@ error: void _PyPegen_clear_memo_statistics(void) { - FT_MUTEX_LOCK(&_PyRuntime.parser.mutex); + PyMutex_Lock(&_PyRuntime.parser.mutex); for (int i = 0; i < NSTATISTICS; i++) { memo_statistics[i] = 0; } - FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex); + PyMutex_Unlock(&_PyRuntime.parser.mutex); } PyObject * @@ -318,22 +317,22 @@ _PyPegen_get_memo_statistics(void) return NULL; } - FT_MUTEX_LOCK(&_PyRuntime.parser.mutex); + PyMutex_Lock(&_PyRuntime.parser.mutex); for (int i = 0; i < NSTATISTICS; i++) { PyObject *value = PyLong_FromLong(memo_statistics[i]); if (value == NULL) { - FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex); + PyMutex_Unlock(&_PyRuntime.parser.mutex); Py_DECREF(ret); return NULL; } // PyList_SetItem borrows a reference to value. if (PyList_SetItem(ret, i, value) < 0) { - FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex); + PyMutex_Unlock(&_PyRuntime.parser.mutex); Py_DECREF(ret); return NULL; } } - FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex); + PyMutex_Unlock(&_PyRuntime.parser.mutex); return ret; } #endif @@ -359,9 +358,9 @@ _PyPegen_is_memoized(Parser *p, int type, void *pres) if (count <= 0) { count = 1; } - FT_MUTEX_LOCK(&_PyRuntime.parser.mutex); + PyMutex_Lock(&_PyRuntime.parser.mutex); memo_statistics[type] += count; - FT_MUTEX_UNLOCK(&_PyRuntime.parser.mutex); + PyMutex_Unlock(&_PyRuntime.parser.mutex); } #endif p->mark = m->mark; From 8d2d7bb2e754f8649a68ce4116271a4932f76907 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" <68491+gpshead@users.noreply.github.com> Date: Sat, 20 Dec 2025 15:42:06 -0800 Subject: [PATCH 62/80] gh-142145: relax the no-longer-quadratic test timing (#143030) * gh-142145: relax the no-longer-quadratic test timing * require cpu resource --- Lib/test/test_minidom.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 7717a98583f..69fae957ec7 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -174,6 +174,7 @@ class MinidomTest(unittest.TestCase): self.assertEqual(dom.documentElement.childNodes[-1].data, "Hello") dom.unlink() + @support.requires_resource('cpu') def testAppendChildNoQuadraticComplexity(self): impl = getDOMImplementation() @@ -182,14 +183,18 @@ class MinidomTest(unittest.TestCase): children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)] element = top_element - start = time.time() + start = time.monotonic() for child in children: element.appendChild(child) element = child - end = time.time() + end = time.monotonic() # This example used to take at least 30 seconds. - self.assertLess(end - start, 1) + # Conservative assertion due to the wide variety of systems and + # build configs timing based tests wind up run under. + # A --with-address-sanitizer --with-pydebug build on a rpi5 still + # completes this loop in <0.5 seconds. + self.assertLess(end - start, 4) def testSetAttributeNodeWithoutOwnerDocument(self): # regression test for gh-142754 From b8d3fddba6e96e693ced0d3b8f6ddbd61428fd32 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" <68491+gpshead@users.noreply.github.com> Date: Sat, 20 Dec 2025 22:47:40 -0800 Subject: [PATCH 63/80] gh-70647: Better promote how to safely parse yearless dates in datetime. (GH-116179) * gh-70647: Better promote how to safely parse yearless dates in datetime. Every four years people encounter this because it just isn't obvious. This moves the footnote up to a note with a code example. We'd love to change the default year value for datetime but doing that could have other consequences for existing code. This documented workaround *always* works. * doctest code within note is bad, dedent. * Update to match the error message. * remove no longer referenced footnote * ignore the warning in the doctest * use Petr's suggestion for the docs to hide the warning processing * cover date.strptime (3.14) as well --- Doc/library/datetime.rst | 43 ++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 8ae1c1fb9e4..48e7080da6c 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -2651,9 +2651,42 @@ Broadly speaking, ``d.strftime(fmt)`` acts like the :mod:`time` module's ``time.strftime(fmt, d.timetuple())`` although not all objects support a :meth:`~date.timetuple` method. -For the :meth:`.datetime.strptime` class method, the default value is -``1900-01-01T00:00:00.000``: any components not specified in the format string -will be pulled from the default value. [#]_ +For the :meth:`.datetime.strptime` and :meth:`.date.strptime` class methods, +the default value is ``1900-01-01T00:00:00.000``: any components not specified +in the format string will be pulled from the default value. + +.. note:: + When used to parse partial dates lacking a year, :meth:`.datetime.strptime` + and :meth:`.date.strptime` will raise when encountering February 29 because + the default year of 1900 is *not* a leap year. Always add a default leap + year to partial date strings before parsing. + + +.. testsetup:: + + # doctest seems to turn the warning into an error which makes it + # show up and require matching and prevents the actual interesting + # exception from being raised. + # Manually apply the catch_warnings context manager + import warnings + catch_warnings = warnings.catch_warnings() + catch_warnings.__enter__() + warnings.simplefilter("ignore") + +.. testcleanup:: + + catch_warnings.__exit__() + +.. doctest:: + + >>> from datetime import datetime + >>> value = "2/29" + >>> datetime.strptime(value, "%m/%d") + Traceback (most recent call last): + ... + ValueError: day 29 must be in range 1..28 for month 2 in year 1900 + >>> datetime.strptime(f"1904 {value}", "%Y %m/%d") + datetime.datetime(1904, 2, 29, 0, 0) Using ``datetime.strptime(date_string, format)`` is equivalent to:: @@ -2790,7 +2823,7 @@ Notes: include a year in the format. If the value you need to parse lacks a year, append an explicit dummy leap year. Otherwise your code will raise an exception when it encounters leap day because the default year used by the - parser is not a leap year. Users run into this bug every four years... + parser (1900) is not a leap year. Users run into that bug every leap year. .. doctest:: @@ -2817,5 +2850,3 @@ Notes: .. [#] See R. H. van Gent's `guide to the mathematics of the ISO 8601 calendar `_ for a good explanation. - -.. [#] Passing ``datetime.strptime('Feb 29', '%b %d')`` will fail since 1900 is not a leap year. From 09044dd42b50e628b197afb2979afcbe49d4b83f Mon Sep 17 00:00:00 2001 From: saucoide <32314353+saucoide@users.noreply.github.com> Date: Sun, 21 Dec 2025 17:58:07 +0100 Subject: [PATCH 64/80] gh-80744: do not read .pdbrc twice when cwd == $home (#136816) --- Lib/pdb.py | 19 ++++++++++++------- Lib/test/test_pdb.py | 17 +++++++++++++++++ ...5-12-20-16-35-42.gh-issue-80744.X4pZ2N.rst | 1 + 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-20-16-35-42.gh-issue-80744.X4pZ2N.rst diff --git a/Lib/pdb.py b/Lib/pdb.py index 4a6bc17e91c..eee0273fdc4 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -391,17 +391,22 @@ class Pdb(bdb.Bdb, cmd.Cmd): # Read ~/.pdbrc and ./.pdbrc self.rcLines = [] if readrc: + home_rcfile = os.path.expanduser("~/.pdbrc") + local_rcfile = os.path.abspath(".pdbrc") + try: - with open(os.path.expanduser('~/.pdbrc'), encoding='utf-8') as rcFile: - self.rcLines.extend(rcFile) - except OSError: - pass - try: - with open(".pdbrc", encoding='utf-8') as rcFile: - self.rcLines.extend(rcFile) + with open(home_rcfile, encoding='utf-8') as rcfile: + self.rcLines.extend(rcfile) except OSError: pass + if local_rcfile != home_rcfile: + try: + with open(local_rcfile, encoding='utf-8') as rcfile: + self.rcLines.extend(rcfile) + except OSError: + pass + self.commands = {} # associates a command list to breakpoint numbers self.commands_defining = False # True while in the process of defining # a command list diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 4352aa6abfe..0e23cd66043 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -4093,6 +4093,23 @@ def bœr(): f.write("invalid") self.assertEqual(pdb.Pdb().rcLines[0], "invalid") + def test_readrc_current_dir(self): + with os_helper.temp_cwd() as cwd: + rc_path = os.path.join(cwd, ".pdbrc") + with open(rc_path, "w") as f: + f.write("invalid") + self.assertEqual(pdb.Pdb().rcLines[-1], "invalid") + + def test_readrc_cwd_is_home(self): + with os_helper.EnvironmentVarGuard() as env: + env.unset("HOME") + with os_helper.temp_cwd() as cwd, patch("os.path.expanduser"): + rc_path = os.path.join(cwd, ".pdbrc") + os.path.expanduser.return_value = rc_path + with open(rc_path, "w") as f: + f.write("invalid") + self.assertEqual(pdb.Pdb().rcLines, ["invalid"]) + def test_header(self): stdout = StringIO() header = 'Nobody expects... blah, blah, blah' diff --git a/Misc/NEWS.d/next/Library/2025-12-20-16-35-42.gh-issue-80744.X4pZ2N.rst b/Misc/NEWS.d/next/Library/2025-12-20-16-35-42.gh-issue-80744.X4pZ2N.rst new file mode 100644 index 00000000000..03ec9e4652b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-20-16-35-42.gh-issue-80744.X4pZ2N.rst @@ -0,0 +1 @@ +Fix issue where ``pdb`` would read a ``.pdbrc`` twice if launched from the home directory From 6213a512bf42464e35ae5090358b80aaa64904cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Mon, 22 Dec 2025 08:05:15 +0100 Subject: [PATCH 65/80] gh-143046: Make asyncio REPL respect the `-q` flag (quiet mode) (#143047) --- Lib/asyncio/__main__.py | 15 ++++++++------- Lib/test/test_repl.py | 6 ++++++ ...2025-12-21-17-44-28.gh-issue-143046.GBa5Ip.rst | 2 ++ 3 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-21-17-44-28.gh-issue-143046.GBa5Ip.rst diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 89d456b6858..afbb70bbcab 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -86,14 +86,15 @@ class REPLThread(threading.Thread): global return_code try: - banner = ( - f'asyncio REPL {sys.version} on {sys.platform}\n' - f'Use "await" directly instead of "asyncio.run()".\n' - f'Type "help", "copyright", "credits" or "license" ' - f'for more information.\n' - ) + if not sys.flags.quiet: + banner = ( + f'asyncio REPL {sys.version} on {sys.platform}\n' + f'Use "await" directly instead of "asyncio.run()".\n' + f'Type "help", "copyright", "credits" or "license" ' + f'for more information.\n' + ) - console.write(banner) + console.write(banner) if startup_path := os.getenv("PYTHONSTARTUP"): sys.audit("cpython.run_startup", startup_path) diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index 042aa84b35d..0fa1df40e44 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -409,6 +409,12 @@ class TestAsyncioREPL(unittest.TestCase): expected = "toplevel contextvar test: ok" self.assertIn(expected, output, expected) + def test_quiet_mode(self): + p = spawn_repl("-q", "-m", "asyncio", custom=True) + output = kill_python(p) + self.assertEqual(p.returncode, 0) + self.assertEqual(output[:3], ">>>") + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-12-21-17-44-28.gh-issue-143046.GBa5Ip.rst b/Misc/NEWS.d/next/Library/2025-12-21-17-44-28.gh-issue-143046.GBa5Ip.rst new file mode 100644 index 00000000000..ac819a47f4c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-21-17-44-28.gh-issue-143046.GBa5Ip.rst @@ -0,0 +1,2 @@ +The :mod:`asyncio` REPL no longer prints copyright and version messages in +the quiet mode (:option:`-q`). Patch by Bartosz Sławecki. From 39608781437e3ab9b3be88bfdb99ac0e4ac78576 Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Mon, 22 Dec 2025 18:16:50 +0800 Subject: [PATCH 66/80] Remove unreachable code in mmapmodule error path on Windows (GH-143063) mmapmodule: remove unreachable code in Windows error path Remove an unreachable `return NULL` after `PyErr_SetFromWindowsErr()` in the Windows mmap resize error path. Signed-off-by: Yongtao Huang --- Modules/mmapmodule.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index ea20fb29c90..0928ea6a8b7 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -963,7 +963,6 @@ mmap_mmap_resize_impl(mmap_object *self, Py_ssize_t new_size) if (error) { return PyErr_SetFromWindowsErr(error); - return NULL; } /* It's possible for a resize to fail, typically because another mapping is still held against the same underlying file. Even if nothing has From ff7f62eb2333ac2a2ce2726ba1763bf2fa1956e2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 22 Dec 2025 16:15:57 +0200 Subject: [PATCH 67/80] gh-142927: Tachyon: Comma separate thousands and fix singular/plurals (#142934) --- Lib/profiling/sampling/_format_utils.py | 5 ++ .../sampling/_heatmap_assets/heatmap.js | 4 +- Lib/profiling/sampling/cli.py | 11 +++++ Lib/profiling/sampling/heatmap_collector.py | 46 ++++++++++--------- Lib/profiling/sampling/sample.py | 44 ++++++++---------- 5 files changed, 64 insertions(+), 46 deletions(-) create mode 100644 Lib/profiling/sampling/_format_utils.py diff --git a/Lib/profiling/sampling/_format_utils.py b/Lib/profiling/sampling/_format_utils.py new file mode 100644 index 00000000000..237a4f4186b --- /dev/null +++ b/Lib/profiling/sampling/_format_utils.py @@ -0,0 +1,5 @@ +import locale + + +def fmt(value: int | float, decimals: int = 1) -> str: + return locale.format_string(f'%.{decimals}f', value, grouping=True) diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap.js b/Lib/profiling/sampling/_heatmap_assets/heatmap.js index 90b5b111d36..53928b7b20f 100644 --- a/Lib/profiling/sampling/_heatmap_assets/heatmap.js +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap.js @@ -577,10 +577,12 @@ function populateBytecodePanel(panel, button) { else if (specPct >= 33) specClass = 'medium'; // Build specialization summary + const instruction_word = instructions.length === 1 ? 'instruction' : 'instructions'; + const sample_word = totalSamples === 1 ? 'sample' : 'samples'; let html = `
${specPct}% specialized - (${specializedCount}/${instructions.length} instructions, ${specializedSamples.toLocaleString()}/${totalSamples.toLocaleString()} samples) + (${specializedCount}/${instructions.length} ${instruction_word}, ${specializedSamples.toLocaleString()}/${totalSamples.toLocaleString()} ${sample_word})
`; html += '
' + diff --git a/Lib/profiling/sampling/cli.py b/Lib/profiling/sampling/cli.py index 554167e43f5..aacec645c34 100644 --- a/Lib/profiling/sampling/cli.py +++ b/Lib/profiling/sampling/cli.py @@ -2,6 +2,7 @@ import argparse import importlib.util +import locale import os import selectors import socket @@ -634,6 +635,16 @@ def _validate_args(args, parser): def main(): """Main entry point for the CLI.""" + # Set locale for number formatting, restore on exit + old_locale = locale.setlocale(locale.LC_ALL, None) + locale.setlocale(locale.LC_ALL, "") + try: + _main() + finally: + locale.setlocale(locale.LC_ALL, old_locale) + + +def _main(): # Create the main parser parser = argparse.ArgumentParser( description=_HELP_DESCRIPTION, diff --git a/Lib/profiling/sampling/heatmap_collector.py b/Lib/profiling/sampling/heatmap_collector.py index e6701901aa3..d9dabd664b3 100644 --- a/Lib/profiling/sampling/heatmap_collector.py +++ b/Lib/profiling/sampling/heatmap_collector.py @@ -5,6 +5,7 @@ import collections import html import importlib.resources import json +import locale import math import os import platform @@ -15,6 +16,7 @@ from pathlib import Path from typing import Dict, List, Tuple from ._css_utils import get_combined_css +from ._format_utils import fmt from .collector import normalize_location, extract_lineno from .stack_collector import StackTraceCollector @@ -343,7 +345,7 @@ class _HtmlRenderer:
{icon} {type_names[module_type]} - ({tree.count} {file_word}, {tree.samples:,} {sample_word}) + ({tree.count} {file_word}, {tree.samples:n} {sample_word})
''' @@ -390,7 +392,7 @@ class _HtmlRenderer: parts.append(f'{indent} ') parts.append(f'{indent} 📁 {html.escape(name)}') parts.append(f'{indent} ' - f'({node.count} {file_word}, {node.samples:,} {sample_word})') + f'({node.count} {file_word}, {node.samples:n} {sample_word})') parts.append(f'{indent}
') parts.append(f'{indent}