From c3cd21c861a8850c4f42f5e1fde8e634a3ce4a06 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Thu, 24 Apr 2025 22:56:29 +0200 Subject: [PATCH 1/4] Make _BINARY_OP_SUBSCR_TUPLE_INT smarter --- Lib/test/test_capi/test_opt.py | 17 +++++++++++++++++ Python/optimizer_bytecodes.c | 14 ++++++++++++++ Python/optimizer_cases.c.h | 16 +++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 0047306ae422db..7e0c60d5522402 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1923,6 +1923,23 @@ def testfunc(n): self.assertNotIn("_GUARD_TOS_INT", uops) self.assertIn("_CALL_LEN", uops) + def test_binary_op_subscr_tuple_int(self): + def testfunc(n): + x = 0 + for _ in range(n): + y = (1, 2) + if y[0] == 1: # _COMPARE_OP_INT + _GUARD_IS_TRUE_POP are removed + 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_TUPLE_INT", uops) + self.assertNotIn("_COMPARE_OP_INT", uops) + self.assertNotIn("_GUARD_IS_TRUE_POP", uops) + def global_identity(x): return x diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 040e54479b722a..7ef41c7aa811a6 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -370,6 +370,20 @@ dummy_func(void) { res = sym_new_type(ctx, &PyUnicode_Type); } + op(_BINARY_OP_SUBSCR_TUPLE_INT, (left, right -- res)) { + assert(sym_matches_type(left, &PyTuple_Type)); + if (sym_is_const(ctx, right)) { + assert(PyLong_CheckExact(sym_get_const(ctx, right))); + long index = PyLong_AsLong(sym_get_const(ctx, right)); + assert(index >= 0); + assert(index < sym_tuple_length(left)); + res = sym_tuple_getitem(ctx, left, index); + } + else { + res = sym_new_not_null(ctx); + } + } + op(_TO_BOOL, (value -- res)) { int already_bool = optimize_to_bool(this_instr, ctx, value, &res); if (!already_bool) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 9a5a362ec199a9..3ad68834032fd7 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -617,8 +617,22 @@ } case _BINARY_OP_SUBSCR_TUPLE_INT: { + JitOptSymbol *right; + JitOptSymbol *left; JitOptSymbol *res; - res = sym_new_not_null(ctx); + right = stack_pointer[-1]; + left = stack_pointer[-2]; + assert(sym_matches_type(left, &PyTuple_Type)); + if (sym_is_const(ctx, right)) { + assert(PyLong_CheckExact(sym_get_const(ctx, right))); + long index = PyLong_AsLong(sym_get_const(ctx, right)); + assert(index >= 0); + assert(index < sym_tuple_length(left)); + res = sym_tuple_getitem(ctx, left, index); + } + else { + res = sym_new_not_null(ctx); + } stack_pointer[-2] = res; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); From c7b7b484ba31e0c9952ade35299183e72f2b2658 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sat, 26 Apr 2025 13:59:30 +0200 Subject: [PATCH 2/4] Add news entry --- .../2025-04-26-13-57-13.gh-issue-131798.Gt8CGE.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-13-57-13.gh-issue-131798.Gt8CGE.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-13-57-13.gh-issue-131798.Gt8CGE.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-13-57-13.gh-issue-131798.Gt8CGE.rst new file mode 100644 index 00000000000000..f4049240f7d15c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-13-57-13.gh-issue-131798.Gt8CGE.rst @@ -0,0 +1,2 @@ +Propagate the return type of ``_BINARY_OP_SUBSCR_TUPLE_INT`` in JIT. Patch +by Tomas Roun From dddd56ff271e69215bea67516a88d3dfe99b5db7 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sat, 26 Apr 2025 20:11:43 +0200 Subject: [PATCH 3/4] Fix a case when the tuple length is not known --- Python/optimizer_bytecodes.c | 11 +++++++++-- Python/optimizer_cases.c.h | 10 ++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 7ef41c7aa811a6..8ae1e4dd65445a 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -376,8 +376,15 @@ dummy_func(void) { assert(PyLong_CheckExact(sym_get_const(ctx, right))); long index = PyLong_AsLong(sym_get_const(ctx, right)); assert(index >= 0); - assert(index < sym_tuple_length(left)); - res = sym_tuple_getitem(ctx, left, index); + int tuple_length = sym_tuple_length(left); + if (tuple_length == -1) { + // Unknown length + res = sym_new_not_null(ctx); + } + else { + assert(index < tuple_length); + res = sym_tuple_getitem(ctx, left, index); + } } else { res = sym_new_not_null(ctx); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 3ad68834032fd7..5138fe2903e82a 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -627,8 +627,14 @@ assert(PyLong_CheckExact(sym_get_const(ctx, right))); long index = PyLong_AsLong(sym_get_const(ctx, right)); assert(index >= 0); - assert(index < sym_tuple_length(left)); - res = sym_tuple_getitem(ctx, left, index); + int tuple_length = sym_tuple_length(left); + if (tuple_length == -1) { + res = sym_new_not_null(ctx); + } + else { + assert(index < tuple_length); + res = sym_tuple_getitem(ctx, left, index); + } } else { res = sym_new_not_null(ctx); From 00b00cbfe82d95ec15ad0945c83a3553646538a0 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sat, 26 Apr 2025 20:13:19 +0200 Subject: [PATCH 4/4] Keep the same names for stack variables --- Python/optimizer_bytecodes.c | 14 +++++++------- Python/optimizer_cases.c.h | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 8ae1e4dd65445a..f862c9c8c6a840 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -370,20 +370,20 @@ dummy_func(void) { res = sym_new_type(ctx, &PyUnicode_Type); } - op(_BINARY_OP_SUBSCR_TUPLE_INT, (left, right -- res)) { - assert(sym_matches_type(left, &PyTuple_Type)); - if (sym_is_const(ctx, right)) { - assert(PyLong_CheckExact(sym_get_const(ctx, right))); - long index = PyLong_AsLong(sym_get_const(ctx, right)); + op(_BINARY_OP_SUBSCR_TUPLE_INT, (tuple_st, sub_st -- res)) { + assert(sym_matches_type(tuple_st, &PyTuple_Type)); + if (sym_is_const(ctx, sub_st)) { + assert(PyLong_CheckExact(sym_get_const(ctx, sub_st))); + long index = PyLong_AsLong(sym_get_const(ctx, sub_st)); assert(index >= 0); - int tuple_length = sym_tuple_length(left); + int tuple_length = sym_tuple_length(tuple_st); if (tuple_length == -1) { // Unknown length res = sym_new_not_null(ctx); } else { assert(index < tuple_length); - res = sym_tuple_getitem(ctx, left, index); + res = sym_tuple_getitem(ctx, tuple_st, index); } } else { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 5138fe2903e82a..c92b036eb56463 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -617,23 +617,23 @@ } case _BINARY_OP_SUBSCR_TUPLE_INT: { - JitOptSymbol *right; - JitOptSymbol *left; + JitOptSymbol *sub_st; + JitOptSymbol *tuple_st; JitOptSymbol *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - assert(sym_matches_type(left, &PyTuple_Type)); - if (sym_is_const(ctx, right)) { - assert(PyLong_CheckExact(sym_get_const(ctx, right))); - long index = PyLong_AsLong(sym_get_const(ctx, right)); + sub_st = stack_pointer[-1]; + tuple_st = stack_pointer[-2]; + assert(sym_matches_type(tuple_st, &PyTuple_Type)); + if (sym_is_const(ctx, sub_st)) { + assert(PyLong_CheckExact(sym_get_const(ctx, sub_st))); + long index = PyLong_AsLong(sym_get_const(ctx, sub_st)); assert(index >= 0); - int tuple_length = sym_tuple_length(left); + int tuple_length = sym_tuple_length(tuple_st); if (tuple_length == -1) { res = sym_new_not_null(ctx); } else { assert(index < tuple_length); - res = sym_tuple_getitem(ctx, left, index); + res = sym_tuple_getitem(ctx, tuple_st, index); } } else {