From 06f1e374e292e929aad1f60b056c468e4b5b329e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 10:20:57 -0800 Subject: [PATCH 01/17] Rename get_{uop,counter}_optimizer to new_*_optimizer --- Lib/test/test_capi/test_opt.py | 72 +++++++++++++++++----------------- Lib/test/test_monitoring.py | 2 +- Modules/_testinternalcapi.c | 8 ++-- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 1a8ed3441fa855..8381b4187f3208 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -32,16 +32,16 @@ def clear_executors(func): class TestOptimizerAPI(unittest.TestCase): - def test_get_counter_optimizer_dealloc(self): + def test_new_counter_optimizer_dealloc(self): # See gh-108727 def f(): - _testinternalcapi.get_counter_optimizer() + _testinternalcapi.new_counter_optimizer() f() def test_get_set_optimizer(self): old = _testinternalcapi.get_optimizer() - opt = _testinternalcapi.get_counter_optimizer() + opt = _testinternalcapi.new_counter_optimizer() try: _testinternalcapi.set_optimizer(opt) self.assertEqual(_testinternalcapi.get_optimizer(), opt) @@ -62,7 +62,7 @@ def loop(): loop = ns['loop'] for repeat in range(5): - opt = _testinternalcapi.get_counter_optimizer() + opt = _testinternalcapi.new_counter_optimizer() with temporary_optimizer(opt): self.assertEqual(opt.get_count(), 0) with clear_executors(loop): @@ -90,7 +90,7 @@ def long_loop(): """), ns, ns) long_loop = ns['long_loop'] - opt = _testinternalcapi.get_counter_optimizer() + opt = _testinternalcapi.new_counter_optimizer() with temporary_optimizer(opt): self.assertEqual(opt.get_count(), 0) long_loop() @@ -102,7 +102,7 @@ def testfunc(x): while i < x: i += 1 - opt = _testinternalcapi.get_counter_optimizer() + opt = _testinternalcapi.new_counter_optimizer() with temporary_optimizer(opt): testfunc(1000) code, replace_code = testfunc.__code__, testfunc.__code__.replace() @@ -127,7 +127,7 @@ class TestExecutorInvalidation(unittest.TestCase): def setUp(self): self.old = _testinternalcapi.get_optimizer() - self.opt = _testinternalcapi.get_counter_optimizer() + self.opt = _testinternalcapi.new_counter_optimizer() _testinternalcapi.set_optimizer(self.opt) def tearDown(self): @@ -176,7 +176,7 @@ def f(): pass """), ns, ns) f = ns['f'] - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): f() exe = get_first_executor(f) @@ -189,7 +189,7 @@ def test_sys__clear_internal_caches(self): def f(): for _ in range(1000): pass - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): f() exe = get_first_executor(f) @@ -208,7 +208,7 @@ def testfunc(x): while i < x: i += 1 - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(1000) @@ -255,7 +255,7 @@ def many_vars(): """), ns, ns) many_vars = ns["many_vars"] - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): ex = get_first_executor(many_vars) self.assertIsNone(ex) @@ -277,7 +277,7 @@ def testfunc(x): while i < x: i += 1 - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(20) @@ -293,7 +293,7 @@ def testfunc(n): while i < n: i += 1 - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(20) @@ -308,7 +308,7 @@ def testfunc(a): if x is None: x = 0 - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(range(20)) @@ -324,7 +324,7 @@ def testfunc(a): if x is not None: x = 0 - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(range(20)) @@ -339,7 +339,7 @@ def testfunc(n): while not i >= n: i += 1 - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(20) @@ -354,7 +354,7 @@ def testfunc(n): while i < n: i += 1 - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(20) @@ -374,7 +374,7 @@ def testfunc(n): a += 1 return a - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(20) @@ -392,7 +392,7 @@ def testfunc(n): total += i return total - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): total = testfunc(20) self.assertEqual(total, 190) @@ -413,7 +413,7 @@ def testfunc(a): total += i return total - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): a = list(range(20)) total = testfunc(a) @@ -435,7 +435,7 @@ def testfunc(a): total += i return total - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): a = tuple(range(20)) total = testfunc(a) @@ -455,7 +455,7 @@ def testfunc(it): for x in it: pass - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): a = [1, 2, 3] it = iter(a) @@ -471,7 +471,7 @@ def dummy(x): for i in range(n): dummy(i) - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(20) @@ -489,7 +489,7 @@ def testfunc(n): else: i = 1 - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(20) @@ -517,7 +517,7 @@ def testfunc(n, m): x += 1000*i + j return x - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): x = testfunc(10, 10) @@ -546,7 +546,7 @@ def testfunc(n): bits += 1 return bits - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): x = testfunc(20) @@ -570,7 +570,7 @@ def testfunc(loops): num += 1 return a - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() res = None with temporary_optimizer(opt): res = testfunc(32) @@ -594,7 +594,7 @@ def testfunc(loops): num += 1 return a - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() res = None with temporary_optimizer(opt): res = testfunc(32) @@ -618,7 +618,7 @@ def testfunc(loops): num += 1 return x - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() res = None with temporary_optimizer(opt): res = testfunc(32) @@ -642,7 +642,7 @@ def testfunc(loops): num += 1 return a - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() res = None with temporary_optimizer(opt): res = testfunc(64) @@ -659,7 +659,7 @@ def dummy(x): for i in range(n): dummy(i) - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(32) @@ -677,7 +677,7 @@ def testfunc(n): x = i + i return x - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): res = testfunc(32) @@ -699,7 +699,7 @@ def testfunc(n): res = x + z + a + b return res - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): res = testfunc(32) @@ -716,7 +716,7 @@ def testfunc(n): for _ in range(n): return [i for i in range(n)] - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() with temporary_optimizer(opt): testfunc(32) @@ -733,7 +733,7 @@ def testfunc(n): for i in range(n): dummy(i) - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() # Trigger specialization testfunc(8) with temporary_optimizer(opt): @@ -773,7 +773,7 @@ def testfunc(n): x = range(i) return x - opt = _testinternalcapi.get_uop_optimizer() + opt = _testinternalcapi.new_uop_optimizer() _testinternalcapi.set_optimizer(opt) testfunc(64) diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index 60b6326bfbad5e..58aa4bca7534b1 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -1799,7 +1799,7 @@ class TestOptimizer(MonitoringTestBase, unittest.TestCase): def setUp(self): import _testinternalcapi self.old_opt = _testinternalcapi.get_optimizer() - opt = _testinternalcapi.get_counter_optimizer() + opt = _testinternalcapi.new_counter_optimizer() _testinternalcapi.set_optimizer(opt) super(TestOptimizer, self).setUp() diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 3834f00009cea4..dcae95401e9503 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -960,13 +960,13 @@ iframe_getlasti(PyObject *self, PyObject *frame) } static PyObject * -get_counter_optimizer(PyObject *self, PyObject *arg) +new_counter_optimizer(PyObject *self, PyObject *arg) { return PyUnstable_Optimizer_NewCounter(); } static PyObject * -get_uop_optimizer(PyObject *self, PyObject *arg) +new_uop_optimizer(PyObject *self, PyObject *arg) { return PyUnstable_Optimizer_NewUOpOptimizer(); } @@ -1709,8 +1709,8 @@ static PyMethodDef module_functions[] = { {"get_optimizer", get_optimizer, METH_NOARGS, NULL}, {"set_optimizer", set_optimizer, METH_O, NULL}, {"get_executor", _PyCFunction_CAST(get_executor), METH_FASTCALL, NULL}, - {"get_counter_optimizer", get_counter_optimizer, METH_NOARGS, NULL}, - {"get_uop_optimizer", get_uop_optimizer, METH_NOARGS, NULL}, + {"new_counter_optimizer", new_counter_optimizer, METH_NOARGS, NULL}, + {"new_uop_optimizer", new_uop_optimizer, METH_NOARGS, NULL}, {"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL}, {"invalidate_executors", invalidate_executors, METH_O, NULL}, {"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc), From 182c023ef36b5327bf20ddacc91846a93a5fcb34 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 10:32:34 -0800 Subject: [PATCH 02/17] Remove unneeded header from specialize.c --- Python/specialize.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/specialize.c b/Python/specialize.c index 2256d79b387c56..1269bb62f8c90f 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -10,7 +10,6 @@ #include "pycore_moduleobject.h" #include "pycore_object.h" #include "pycore_opcode_metadata.h" // _PyOpcode_Caches -#include "pycore_uop_metadata.h" // _PyOpcode_uop_name #include "pycore_opcode_utils.h" // RESUME_AT_FUNC_START #include "pycore_pylifecycle.h" // _PyOS_URandomNonblock() #include "pycore_runtime.h" // _Py_ID() From d862825ddb57f69a242edcab61b63bdad4c2c104 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 10:33:10 -0800 Subject: [PATCH 03/17] Use _PyUOpName() instead of _PyOpcode_uop_name[] --- Python/optimizer_analysis.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index d73bc310345f41..ce2d5a8289ee39 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -44,6 +44,7 @@ #define MAX_ABSTRACT_FRAME_DEPTH (TRACE_STACK_SIZE + 2) #ifdef Py_DEBUG + extern const char *_PyUOpName(int index); static const char *const DEBUG_ENV = "PYTHON_OPT_DEBUG"; static inline int get_lltrace(void) { char *uop_debug = Py_GETENV(DEBUG_ENV); @@ -631,7 +632,7 @@ uop_redundancy_eliminator( _Py_UOpsSymType **stack_pointer = ctx->frame->stack_pointer; DPRINTF(3, "Abstract interpreting %s:%d ", - _PyOpcode_uop_name[opcode], + _PyUOpName(opcode), oparg); switch (opcode) { #include "tier2_redundancy_eliminator_cases.c.h" From 6dc477df2ec60085dd88d4b6caf5f0b8cfa31b69 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 10:48:26 -0800 Subject: [PATCH 04/17] Add target to executor iterator items --- Lib/test/test_capi/test_opt.py | 66 +++++++++++++++++++--------------- Python/optimizer.c | 11 ++++-- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 8381b4187f3208..f89f02f8e5a4f3 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -123,6 +123,10 @@ def get_first_executor(func): return None +def get_opnames(ex): + return {opname for opname, _, _, _ in ex} + + class TestExecutorInvalidation(unittest.TestCase): def setUp(self): @@ -214,7 +218,7 @@ def testfunc(x): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_SET_IP", uops) self.assertIn("_LOAD_FAST", uops) @@ -263,7 +267,8 @@ def many_vars(): ex = get_first_executor(many_vars) self.assertIsNotNone(ex) - self.assertIn(("_LOAD_FAST", 259, 0), list(ex)) + self.assertTrue(any((opcode, oparg, operand) == ("_LOAD_FAST", 259, 0) + for opcode, oparg, _, operand in list(ex))) def test_unspecialized_unpack(self): # An example of an unspecialized opcode @@ -284,7 +289,7 @@ def testfunc(x): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_UNPACK_SEQUENCE", uops) def test_pop_jump_if_false(self): @@ -299,7 +304,7 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_GUARD_IS_TRUE_POP", uops) def test_pop_jump_if_none(self): @@ -314,7 +319,7 @@ def testfunc(a): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_GUARD_IS_NOT_NONE_POP", uops) def test_pop_jump_if_not_none(self): @@ -330,7 +335,7 @@ def testfunc(a): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_GUARD_IS_NONE_POP", uops) def test_pop_jump_if_true(self): @@ -345,7 +350,7 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_GUARD_IS_FALSE_POP", uops) def test_jump_backward(self): @@ -360,7 +365,7 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_JUMP_TO_TOP", uops) def test_jump_forward(self): @@ -380,7 +385,7 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) # Since there is no JUMP_FORWARD instruction, # look for indirect evidence: the += operator self.assertIn("_BINARY_OP_ADD_INT", uops) @@ -401,7 +406,7 @@ def testfunc(n): self.assertIsNotNone(ex) # for i, (opname, oparg) in enumerate(ex): # print(f"{i:4d}: {opname:<20s} {oparg:3d}") - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_GUARD_NOT_EXHAUSTED_RANGE", uops) # Verification that the jump goes past END_FOR # is done by manual inspection of the output @@ -423,7 +428,7 @@ def testfunc(a): self.assertIsNotNone(ex) # for i, (opname, oparg) in enumerate(ex): # print(f"{i:4d}: {opname:<20s} {oparg:3d}") - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_GUARD_NOT_EXHAUSTED_LIST", uops) # Verification that the jump goes past END_FOR # is done by manual inspection of the output @@ -445,7 +450,7 @@ def testfunc(a): self.assertIsNotNone(ex) # for i, (opname, oparg) in enumerate(ex): # print(f"{i:4d}: {opname:<20s} {oparg:3d}") - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_GUARD_NOT_EXHAUSTED_TUPLE", uops) # Verification that the jump goes past END_FOR # is done by manual inspection of the output @@ -477,7 +482,7 @@ def dummy(x): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_PUSH_FRAME", uops) self.assertIn("_BINARY_OP_ADD_INT", uops) @@ -495,7 +500,7 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_GUARD_IS_FALSE_POP", uops) def test_for_iter_tier_two(self): @@ -525,7 +530,7 @@ def testfunc(n, m): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_FOR_ITER_TIER_TWO", uops) def test_confidence_score(self): @@ -553,7 +558,7 @@ def testfunc(n): self.assertEqual(x, 40) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - ops = [opname for opname, _, _ in ex] + ops = [opname for opname, _, _, _ in ex] count = ops.count("_GUARD_IS_TRUE_POP") # Because Each 'if' halves the score, the second branch is # too much already. @@ -578,8 +583,8 @@ def testfunc(loops): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) self.assertEqual(res, 63) - binop_count = [opname for opname, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] - guard_both_int_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_INT"] + binop_count = [opname for opname, _, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] + guard_both_int_count = [opname for opname, _, _, _ in ex if opname == "_GUARD_BOTH_INT"] self.assertGreaterEqual(len(binop_count), 3) self.assertLessEqual(len(guard_both_int_count), 1) @@ -602,8 +607,8 @@ def testfunc(loops): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) self.assertEqual(res, 124) - binop_count = [opname for opname, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] - guard_both_int_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_INT"] + binop_count = [opname for opname, _, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] + guard_both_int_count = [opname for opname, _, _, _ in ex if opname == "_GUARD_BOTH_INT"] self.assertGreaterEqual(len(binop_count), 3) self.assertLessEqual(len(guard_both_int_count), 1) @@ -626,8 +631,8 @@ def testfunc(loops): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) self.assertEqual(res, 124) - binop_count = [opname for opname, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] - guard_both_int_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_INT"] + binop_count = [opname for opname, _, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] + guard_both_int_count = [opname for opname, _, _, _ in ex if opname == "_GUARD_BOTH_INT"] self.assertGreaterEqual(len(binop_count), 3) self.assertLessEqual(len(guard_both_int_count), 1) @@ -649,7 +654,7 @@ def testfunc(loops): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - binop_count = [opname for opname, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] + binop_count = [opname for opname, _, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] self.assertGreaterEqual(len(binop_count), 3) def test_call_py_exact_args(self): @@ -665,7 +670,7 @@ def dummy(x): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_PUSH_FRAME", uops) self.assertIn("_BINARY_OP_ADD_INT", uops) self.assertNotIn("_CHECK_PEP_523", uops) @@ -684,7 +689,7 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertEqual(res, 62) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertNotIn("_GUARD_BOTH_INT", uops) def test_int_value_numbering(self): @@ -706,9 +711,9 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertEqual(res, 4) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertIn("_GUARD_BOTH_INT", uops) - guard_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_INT"] + guard_count = [opname for opname, _, _, _ in ex if opname == "_GUARD_BOTH_INT"] self.assertEqual(len(guard_count), 1) def test_comprehension(self): @@ -722,7 +727,7 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) self.assertNotIn("_BINARY_OP_ADD_INT", uops) def test_call_py_exact_args_disappearing(self): @@ -768,6 +773,9 @@ def get_first_executor(func): pass return None + def get_opnames(ex): + return {opname for opname, _, _, _ in ex} + def testfunc(n): for i in range(n): x = range(i) @@ -779,7 +787,7 @@ def testfunc(n): ex = get_first_executor(testfunc) assert ex is not None - uops = {opname for opname, _, _ in ex} + uops = get_opnames(ex) assert "_LOAD_GLOBAL_BUILTINS" not in uops assert "_LOAD_CONST_INLINE_BORROW_WITH_NULL" in uops """)) diff --git a/Python/optimizer.c b/Python/optimizer.c index efa19680c9b1f3..bc34421e8b1d62 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -277,14 +277,21 @@ uop_item(_PyExecutorObject *self, Py_ssize_t index) Py_DECREF(oname); return NULL; } + PyObject *target = PyLong_FromUnsignedLong(self->trace[index].target); + if (oparg == NULL) { + Py_DECREF(oparg); + Py_DECREF(oname); + return NULL; + } PyObject *operand = PyLong_FromUnsignedLongLong(self->trace[index].operand); if (operand == NULL) { + Py_DECREF(target); Py_DECREF(oparg); Py_DECREF(oname); return NULL; } - PyObject *args[3] = { oname, oparg, operand }; - return _PyTuple_FromArraySteal(args, 3); + PyObject *args[4] = { oname, oparg, target, operand }; + return _PyTuple_FromArraySteal(args, 4); } PySequenceMethods uop_as_sequence = { From 65a01df1a690db0b0810f713d5df55683455711c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 11:40:06 -0800 Subject: [PATCH 05/17] Add executor methods to get underlying opcode/oparg --- Python/optimizer.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Python/optimizer.c b/Python/optimizer.c index bc34421e8b1d62..8cf15f4036595d 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -228,8 +228,22 @@ is_valid(PyObject *self, PyObject *Py_UNUSED(ignored)) return PyBool_FromLong(((_PyExecutorObject *)self)->vm_data.valid); } +static PyObject * +get_opcode(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyLong_FromUnsignedLong(((_PyExecutorObject *)self)->vm_data.opcode); +} + +static PyObject * +get_oparg(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyLong_FromUnsignedLong(((_PyExecutorObject *)self)->vm_data.oparg); +} + static PyMethodDef executor_methods[] = { { "is_valid", is_valid, METH_NOARGS, NULL }, + { "get_opcode", get_opcode, METH_NOARGS, NULL }, + { "get_oparg", get_oparg, METH_NOARGS, NULL }, { NULL, NULL }, }; From 044ab1683ac16902232348b703608aa8a9b08c61 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 12:59:48 -0800 Subject: [PATCH 06/17] Use standardized format for printing uops --- Python/optimizer.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 8cf15f4036595d..1ec15e5e676972 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -261,9 +261,34 @@ uop_dealloc(_PyExecutorObject *self) { const char * _PyUOpName(int index) { +#ifdef Py_DEBUG + if (index < 0 || index > MAX_UOP_ID) { + return NULL; + } +#endif return _PyOpcode_uop_name[index]; } +#ifdef Py_DEBUG +void +_PyUopPrint(_PyUOpInstruction *uop) +{ + char buffer[100]; + const char *name = _PyUOpName(uop->opcode); + if (name == NULL) { + snprintf(buffer, 100, "", uop->opcode); + buffer[99] = '\0'; + name = buffer; + } + printf("%s (%d, target=%d, operand=%" PRIx64 ")", + name, + uop->oparg, + uop->target, + (uint64_t)uop->operand); +} +#define UOP_PRINT(LEVEL, UOP) if (lltrace >= (LEVEL)) _PyUopPrint((UOP)) +#endif + static Py_ssize_t uop_len(_PyExecutorObject *self) { @@ -357,17 +382,14 @@ BRANCH_TO_GUARD[4][2] = { #define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \ - DPRINTF(2, \ - " ADD_TO_TRACE(%s, %d, %" PRIu64 ", %d)\n", \ - _PyUOpName(OPCODE), \ - (OPARG), \ - (uint64_t)(OPERAND), \ - TARGET); \ assert(trace_length < max_length); \ trace[trace_length].opcode = (OPCODE); \ trace[trace_length].oparg = (OPARG); \ trace[trace_length].operand = (OPERAND); \ trace[trace_length].target = (TARGET); \ + DPRINTF(2, "ADD_TO_TRACE: "); \ + UOP_PRINT(2, &trace[trace_length]); \ + DPRINTF(2, "\n"); \ trace_length++; #define INSTR_IP(INSTR, CODE) \ @@ -820,12 +842,9 @@ make_executor_from_uops(_PyUOpInstruction *buffer, _PyBloomFilter *dependencies) if (lltrace >= 2) { printf("Optimized executor (length %d):\n", length); for (int i = 0; i < length; i++) { - printf("%4d %s(%d, %d, %" PRIu64 ")\n", - i, - _PyUOpName(executor->trace[i].opcode), - executor->trace[i].oparg, - executor->trace[i].target, - executor->trace[i].operand); + printf("%4d ", i); + _PyUopPrint(&executor->trace[i]); + printf("\n"); } } #endif From 984ed1b2758f1c17dffa47bd3c23a30b7e4e3c73 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 14:32:34 -0800 Subject: [PATCH 07/17] Tweak _PyUOpPrint usage --- Python/optimizer.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 1ec15e5e676972..eb4052192f390a 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -271,7 +271,7 @@ _PyUOpName(int index) #ifdef Py_DEBUG void -_PyUopPrint(_PyUOpInstruction *uop) +_PyUOpPrint(_PyUOpInstruction *uop) { char buffer[100]; const char *name = _PyUOpName(uop->opcode); @@ -286,7 +286,6 @@ _PyUopPrint(_PyUOpInstruction *uop) uop->target, (uint64_t)uop->operand); } -#define UOP_PRINT(LEVEL, UOP) if (lltrace >= (LEVEL)) _PyUopPrint((UOP)) #endif static Py_ssize_t @@ -387,9 +386,11 @@ BRANCH_TO_GUARD[4][2] = { trace[trace_length].oparg = (OPARG); \ trace[trace_length].operand = (OPERAND); \ trace[trace_length].target = (TARGET); \ - DPRINTF(2, "ADD_TO_TRACE: "); \ - UOP_PRINT(2, &trace[trace_length]); \ - DPRINTF(2, "\n"); \ + if (lltrace >= 2) { \ + printf(" ADD_TO_TRACE: "); \ + _PyUOpPrint(&trace[trace_length]); \ + printf("\n"); \ + } \ trace_length++; #define INSTR_IP(INSTR, CODE) \ @@ -842,8 +843,8 @@ make_executor_from_uops(_PyUOpInstruction *buffer, _PyBloomFilter *dependencies) if (lltrace >= 2) { printf("Optimized executor (length %d):\n", length); for (int i = 0; i < length; i++) { - printf("%4d ", i); - _PyUopPrint(&executor->trace[i]); + printf("%4d OPTIMIZED: ", i); + _PyUOpPrint(&executor->trace[i]); printf("\n"); } } From edf843b2830ad4a9fd4cab773d64c7841ca1823d Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 14:56:15 -0800 Subject: [PATCH 08/17] Switch ceval.c to _PyUOpPrint --- Python/ceval.c | 51 +++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 4f208009086191..9b096c3a8976a6 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -648,7 +648,10 @@ static const _Py_CODEUNIT _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = { extern const struct _PyCode_DEF(8) _Py_InitCleanup; -extern const char *_PyUOpName(int index); +#ifdef Py_DEBUG +extern void _PyUOpPrint(_PyUOpInstruction *uop); +#endif + /* Disable unused label warnings. They are handy for debugging, even if computed gotos aren't used. */ @@ -1015,14 +1018,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int for (;;) { uopcode = next_uop->opcode; - DPRINTF(3, - "%4d: uop %s, oparg %d, operand %" PRIu64 ", target %d, stack_level %d\n", - (int)(next_uop - current_executor->trace), - _PyUOpName(uopcode), - next_uop->oparg, - next_uop->operand, - next_uop->target, +#ifdef Py_DEBUG + if (lltrace >= 3) { + printf("%4d uop: ", (int)(next_uop - current_executor->trace)); + _PyUOpPrint(next_uop); + printf(" stack_level=%d\n", (int)(stack_pointer - _PyFrame_Stackbase(frame))); + } +#endif next_uop++; OPT_STAT_INC(uops_executed); UOP_STAT_INC(uopcode, execution_count); @@ -1037,9 +1040,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int default: #ifdef Py_DEBUG { - fprintf(stderr, "Unknown uop %d, oparg %d, operand %" PRIu64 " @ %d\n", - opcode, next_uop[-1].oparg, next_uop[-1].operand, - (int)(next_uop - current_executor->trace - 1)); + printf("Unknown uop: "); + _PyUOpPrint(&next_uop[-1]); + printf(" @ %d\n", (int)(next_uop - current_executor->trace - 1)); Py_FatalError("Unknown uop"); } #else @@ -1067,10 +1070,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int pop_1_error_tier_two: STACK_SHRINK(1); error_tier_two: - DPRINTF(2, "Error: [UOp %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d -> %s]\n", - uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, - (int)(next_uop - current_executor->trace - 1), - _PyOpcode_OpName[frame->instr_ptr->op.code]); +#ifdef Py_DEBUG + if (lltrace >= 2) { + printf("Error: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(" @ %d -> %s]\n", + (int)(next_uop - current_executor->trace - 1), + _PyOpcode_OpName[frame->instr_ptr->op.code]); + } +#endif OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); frame->return_offset = 0; // Don't leave this random _PyFrame_SetStackPointer(frame, stack_pointer); @@ -1080,10 +1088,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // Jump here from DEOPT_IF() deoptimize: next_instr = next_uop[-1].target + _PyCode_CODE(_PyFrame_GetCode(frame)); - DPRINTF(2, "DEOPT: [UOp %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d -> %s]\n", - uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, - (int)(next_uop - current_executor->trace - 1), - _PyOpcode_OpName[frame->instr_ptr->op.code]); +#ifdef Py_DEBUG + if (lltrace >= 2) { + printf("DEOPT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(" @ %d -> %s]\n", + (int)(next_uop - current_executor->trace - 1), + _PyOpcode_OpName[frame->instr_ptr->op.code]); + } +#endif OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); UOP_STAT_INC(uopcode, miss); Py_DECREF(current_executor); From 65c5c04777431acadc84e196ec7d65fea0131ea2 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 15:14:00 -0800 Subject: [PATCH 09/17] Rearrange ADD_TO_TRACE() assignments --- Python/optimizer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index eb4052192f390a..4790ecd8d754c4 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -380,12 +380,13 @@ BRANCH_TO_GUARD[4][2] = { #endif +// Beware: Macro arg order differs from struct member order #define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \ assert(trace_length < max_length); \ trace[trace_length].opcode = (OPCODE); \ trace[trace_length].oparg = (OPARG); \ - trace[trace_length].operand = (OPERAND); \ trace[trace_length].target = (TARGET); \ + trace[trace_length].operand = (OPERAND); \ if (lltrace >= 2) { \ printf(" ADD_TO_TRACE: "); \ _PyUOpPrint(&trace[trace_length]); \ From 527b78eb754b9bfe29acfdf3d03707b9d3fea13c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 15:45:34 -0800 Subject: [PATCH 10/17] Revert "Remove unneeded header from specialize.c" Reason: The header is in fact needed with --enable-pystats. This reverts commit 182c023ef36b5327bf20ddacc91846a93a5fcb34. --- Python/specialize.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Python/specialize.c b/Python/specialize.c index 1269bb62f8c90f..2256d79b387c56 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -10,6 +10,7 @@ #include "pycore_moduleobject.h" #include "pycore_object.h" #include "pycore_opcode_metadata.h" // _PyOpcode_Caches +#include "pycore_uop_metadata.h" // _PyOpcode_uop_name #include "pycore_opcode_utils.h" // RESUME_AT_FUNC_START #include "pycore_pylifecycle.h" // _PyOS_URandomNonblock() #include "pycore_runtime.h" // _Py_ID() From c9677a1748f555d3f4f7b8ac1f7fb722196f16de Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 15:47:17 -0800 Subject: [PATCH 11/17] Use _PyUOpName() in pystats printing code --- Python/specialize.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Python/specialize.c b/Python/specialize.c index 2256d79b387c56..871979d92298b6 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -17,6 +17,7 @@ #include // rand() +extern const char *_PyUOpName(int index); /* For guidance on adding or extending families of instructions see * ./adaptive.md @@ -246,17 +247,12 @@ print_optimization_stats(FILE *out, OptimizationStats *stats) stats->optimizer_failure_reason_no_memory); const char* const* names; - for (int i = 0; i < 512; i++) { - if (i < 256) { - names = _PyOpcode_OpName; - } else { - names = _PyOpcode_uop_name; - } + for (int i = 0; i <= MAX_UOP_ID; i++) { if (stats->opcode[i].execution_count) { - fprintf(out, "uops[%s].execution_count : %" PRIu64 "\n", names[i], stats->opcode[i].execution_count); + fprintf(out, "uops[%s].execution_count : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].execution_count); } if (stats->opcode[i].miss) { - fprintf(out, "uops[%s].specialization.miss : %" PRIu64 "\n", names[i], stats->opcode[i].miss); + fprintf(out, "uops[%s].specialization.miss : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].miss); } } From 7a34e16b93b1bfa766692c2faa7f16fa2b52ab28 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 15:59:56 -0800 Subject: [PATCH 12/17] Fix JIT build (don't use lltrace ifndef Py_DEBUG) --- Python/optimizer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Python/optimizer.c b/Python/optimizer.c index 4790ecd8d754c4..2d2aeafbab5efa 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -381,6 +381,7 @@ BRANCH_TO_GUARD[4][2] = { // Beware: Macro arg order differs from struct member order +#ifdef Py_DEBUG #define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \ assert(trace_length < max_length); \ trace[trace_length].opcode = (OPCODE); \ @@ -393,6 +394,15 @@ BRANCH_TO_GUARD[4][2] = { printf("\n"); \ } \ trace_length++; +#else +#define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \ + assert(trace_length < max_length); \ + trace[trace_length].opcode = (OPCODE); \ + trace[trace_length].oparg = (OPARG); \ + trace[trace_length].target = (TARGET); \ + trace[trace_length].operand = (OPERAND); \ + trace_length++; +#endif #define INSTR_IP(INSTR, CODE) \ ((uint32_t)((INSTR) - ((_Py_CODEUNIT *)(CODE)->co_code_adaptive))) From 94eeb1beda448ba626b7e96218ba696b158e9cf3 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 16:19:07 -0800 Subject: [PATCH 13/17] Don't use snprintf() --- Python/optimizer.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 2d2aeafbab5efa..76f00be771d9c4 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -273,15 +273,14 @@ _PyUOpName(int index) void _PyUOpPrint(_PyUOpInstruction *uop) { - char buffer[100]; const char *name = _PyUOpName(uop->opcode); if (name == NULL) { - snprintf(buffer, 100, "", uop->opcode); - buffer[99] = '\0'; - name = buffer; + printf("", uop->opcode); } - printf("%s (%d, target=%d, operand=%" PRIx64 ")", - name, + else { + printf("%s", name); + } + printf(" (%d, target=%d, operand=%" PRIx64 ")", uop->oparg, uop->target, (uint64_t)uop->operand); From 206b54ae4a269a7cc8f74b0f8af88688bbee29b8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 15 Feb 2024 17:10:28 -0800 Subject: [PATCH 14/17] Fix uop names in older data files in summarize_stats.py --- Tools/scripts/summarize_stats.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index 7891b9cf923d33..1e6563faeefc66 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -100,6 +100,10 @@ def load_raw_data(input: Path) -> RawData: file=sys.stderr, ) continue + # Hack to handle older data files where some uops + # are missing an underscore prefix in their name + if key.startswith("uops[") and key[5:6] != "_": + key = "uops[_" + key[5:] stats[key.strip()] += int(value) stats["__nfiles__"] += 1 From 6b268f821533d4c0855e37371466c56e1d1cabbe Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 16 Feb 2024 13:13:03 -0800 Subject: [PATCH 15/17] Add offsets to ADD_TO_TRACE debug message --- Python/optimizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 76f00be771d9c4..debbf5ec719c5e 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -388,7 +388,7 @@ BRANCH_TO_GUARD[4][2] = { trace[trace_length].target = (TARGET); \ trace[trace_length].operand = (OPERAND); \ if (lltrace >= 2) { \ - printf(" ADD_TO_TRACE: "); \ + printf("%4d ADD_TO_TRACE: ", trace_length); \ _PyUOpPrint(&trace[trace_length]); \ printf("\n"); \ } \ From a4fb372802a30d24c5e00e30d4d24fe3ebc1fd14 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 20 Feb 2024 11:03:06 -0800 Subject: [PATCH 16/17] Fix up after merge --- Python/ceval.c | 14 ++++++++++---- Python/optimizer.c | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 0d0abab0951a48..06c136aeb252c9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -650,7 +650,7 @@ static const _Py_CODEUNIT _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = { extern const struct _PyCode_DEF(8) _Py_InitCleanup; #ifdef Py_DEBUG -extern void _PyUOpPrint(_PyUOpInstruction *uop); +extern void _PyUOpPrint(const _PyUOpInstruction *uop); #endif @@ -1101,9 +1101,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int uint32_t exit_index = next_uop[-1].exit_index; assert(exit_index < current_executor->exit_count); _PyExitData *exit = ¤t_executor->exits[exit_index]; - DPRINTF(2, "SIDE EXIT: [UOp %d (%s), oparg %d, operand %" PRIu64 ", exit %u, temp %d, target %d -> %s]\n", - uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, exit_index, exit->temperature, - exit->target, _PyOpcode_OpName[_PyCode_CODE(_PyFrame_GetCode(frame))[exit->target].op.code]); +#ifdef Py_DEBUG + if (lltrace >= 2) { + printf("SIDE EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %u, temp %d, target %d -> %s]\n", + exit_index, exit->temperature, exit->target, + _PyOpcode_OpName[frame->instr_ptr->op.code]); + } +#endif Py_INCREF(exit->executor); tstate->previous_executor = (PyObject *)current_executor; GOTO_TIER_TWO(exit->executor); diff --git a/Python/optimizer.c b/Python/optimizer.c index 345fcf6e357169..97e99955f2c55b 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -306,7 +306,7 @@ _PyUOpName(int index) #ifdef Py_DEBUG void -_PyUOpPrint(_PyUOpInstruction *uop) +_PyUOpPrint(const _PyUOpInstruction *uop) { const char *name = _PyUOpName(uop->opcode); if (name == NULL) { From eb129220c23cf263c87a0596bc52727e3814e9e3 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 20 Feb 2024 11:10:32 -0800 Subject: [PATCH 17/17] Always do a range check in _PyUOpName --- Python/optimizer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 97e99955f2c55b..74708beea7a53d 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -296,11 +296,9 @@ uop_dealloc(_PyExecutorObject *self) { const char * _PyUOpName(int index) { -#ifdef Py_DEBUG if (index < 0 || index > MAX_UOP_ID) { return NULL; } -#endif return _PyOpcode_uop_name[index]; }