From 61207d263fb6133810da214ff606fff60abb9d85 Mon Sep 17 00:00:00 2001 From: fluhus Date: Wed, 7 May 2025 12:42:29 -0700 Subject: [PATCH 1/6] JIT: Assign type to sliced string --- Lib/test/test_capi/test_opt.py | 3 --- Python/optimizer_bytecodes.c | 12 ++++++++++++ Python/optimizer_cases.c.h | 9 ++++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index cb6eae484149ee..3fcbaf222435de 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -458,7 +458,6 @@ def _run_with_optimizer(self, testfunc, arg): ex = get_first_executor(testfunc) return res, ex - def test_int_type_propagation(self): def testfunc(loops): num = 0 @@ -1655,13 +1654,11 @@ def testfunc(n): self.assertIn("_CONTAINS_OP_DICT", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) - def test_remove_guard_for_known_type_str(self): def f(n): for i in range(n): false = i == TIER2_THRESHOLD empty = "X"[:false] - empty += "" # Make JIT realize this is a string. if empty: return 1 return 0 diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 49c6bfb6c1b01a..6018e5ffef6a7a 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -1216,6 +1216,18 @@ dummy_func(void) { sym_set_const(callable, list_append); } + op(_BINARY_SLICE, (container, start, stop -- res)) { + // Slicing a string always returns a string. + // TODO: We can apply this to lists and tuples as well. + // We'll start with string to simplify the process. + PyTypeObject *type = sym_get_type(container); + if (type == &PyUnicode_Type) { + res = sym_new_type(ctx, type); + } else { + res = sym_new_not_null(ctx); + } + } + // END BYTECODES // } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index bf7ac72d4579e7..652f636b9ab393 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -562,8 +562,15 @@ } case _BINARY_SLICE: { + JitOptSymbol *container; JitOptSymbol *res; - res = sym_new_not_null(ctx); + container = stack_pointer[-3]; + PyTypeObject *type = sym_get_type(container); + if (type == &PyUnicode_Type) { + res = sym_new_type(ctx, type); + } else { + res = sym_new_not_null(ctx); + } stack_pointer[-3] = res; stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); From e2817911272838f16f54ce28cfb71f4ebf1e10db Mon Sep 17 00:00:00 2001 From: fluhus Date: Wed, 7 May 2025 12:44:25 -0700 Subject: [PATCH 2/6] Revert accidental whitespace change --- Lib/test/test_capi/test_opt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 3fcbaf222435de..e671be32aef18e 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -458,6 +458,7 @@ def _run_with_optimizer(self, testfunc, arg): ex = get_first_executor(testfunc) return res, ex + def test_int_type_propagation(self): def testfunc(loops): num = 0 From e45c280486f32765d5d902c59816c8ff67b300d1 Mon Sep 17 00:00:00 2001 From: fluhus Date: Sun, 25 May 2025 12:13:07 -0700 Subject: [PATCH 3/6] JIT: Remove guard for slicing known type list --- Lib/test/test_capi/test_opt.py | 16 ++++++++++++++++ Python/optimizer_bytecodes.c | 7 +++---- Python/optimizer_cases.c.h | 3 ++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index e671be32aef18e..7254b874bc8890 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2217,6 +2217,22 @@ def f(n): self.assertNotIn("_LOAD_ATTR_METHOD_NO_DICT", uops) self.assertNotIn("_LOAD_ATTR_METHOD_LAZY_DICT", uops) + def test_remove_guard_for_slice_list(self): + def f(n): + for i in range(n): + false = i == TIER2_THRESHOLD + sliced = [1, 2, 3][:false] + if sliced: + return 1 + return 0 + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, 0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_TO_BOOL_LIST", uops) + self.assertNotIn("_GUARD_TOS_LIST", uops) + def global_identity(x): return x diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 6018e5ffef6a7a..47e19f936a6712 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -1217,11 +1217,10 @@ dummy_func(void) { } op(_BINARY_SLICE, (container, start, stop -- res)) { - // Slicing a string always returns a string. - // TODO: We can apply this to lists and tuples as well. - // We'll start with string to simplify the process. + // Slicing a string/list always returns the same type. PyTypeObject *type = sym_get_type(container); - if (type == &PyUnicode_Type) { + if (type == &PyUnicode_Type || + type == &PyList_Type) { res = sym_new_type(ctx, type); } else { res = sym_new_not_null(ctx); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 652f636b9ab393..a0e587b94699a1 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -566,7 +566,8 @@ JitOptSymbol *res; container = stack_pointer[-3]; PyTypeObject *type = sym_get_type(container); - if (type == &PyUnicode_Type) { + if (type == &PyUnicode_Type || + type == &PyList_Type) { res = sym_new_type(ctx, type); } else { res = sym_new_not_null(ctx); From 4eade4a0a6fde390b1962ade154ce75bef1e5ffb Mon Sep 17 00:00:00 2001 From: fluhus Date: Sun, 25 May 2025 12:24:32 -0700 Subject: [PATCH 4/6] JIT: Remove guard for slicing known type tuple --- Lib/test/test_capi/test_opt.py | 12 ++++++++++++ Python/optimizer_bytecodes.c | 5 +++-- Python/optimizer_cases.c.h | 3 ++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 7254b874bc8890..9fb12844b28c55 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2233,6 +2233,18 @@ def f(n): self.assertIn("_TO_BOOL_LIST", uops) self.assertNotIn("_GUARD_TOS_LIST", uops) + def test_remove_guard_for_slice_tuple(self): + def f(n): + for i in range(n): + false = i == TIER2_THRESHOLD + a, b = (1, 2, 3)[: false + 2] + + _, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops) + self.assertNotIn("_GUARD_TOS_TUPLE", uops) + def global_identity(x): return x diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 47e19f936a6712..41018a2d9a5e4b 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -1217,10 +1217,11 @@ dummy_func(void) { } op(_BINARY_SLICE, (container, start, stop -- res)) { - // Slicing a string/list always returns the same type. + // Slicing a string/list/tuple always returns the same type. PyTypeObject *type = sym_get_type(container); if (type == &PyUnicode_Type || - type == &PyList_Type) { + type == &PyList_Type || + type == &PyTuple_Type) { res = sym_new_type(ctx, type); } else { res = sym_new_not_null(ctx); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index a0e587b94699a1..88cb3e51308bfb 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -567,7 +567,8 @@ container = stack_pointer[-3]; PyTypeObject *type = sym_get_type(container); if (type == &PyUnicode_Type || - type == &PyList_Type) { + type == &PyList_Type || + type == &PyTuple_Type) { res = sym_new_type(ctx, type); } else { res = sym_new_not_null(ctx); From a2bba4589a1919f93900750123c3d85185a9f0aa Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 19:32:16 +0000 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-05-25-19-32-15.gh-issue-131798.f5h8aI.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-25-19-32-15.gh-issue-131798.f5h8aI.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-25-19-32-15.gh-issue-131798.f5h8aI.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-25-19-32-15.gh-issue-131798.f5h8aI.rst new file mode 100644 index 00000000000000..6ecbfb8d9cf7df --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-25-19-32-15.gh-issue-131798.f5h8aI.rst @@ -0,0 +1 @@ +Make the JIT optimizer understand that slicing a string/list/tuple returns the same type. From 2bb095bdf279934045cb2656bba685f2455e6606 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 6 Jun 2025 16:48:51 -0700 Subject: [PATCH 6/6] PEP 7 --- Python/optimizer_bytecodes.c | 6 ++++-- Python/optimizer_cases.c.h | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 41018a2d9a5e4b..fa02adfbf8f9e5 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -1221,9 +1221,11 @@ dummy_func(void) { PyTypeObject *type = sym_get_type(container); if (type == &PyUnicode_Type || type == &PyList_Type || - type == &PyTuple_Type) { + type == &PyTuple_Type) + { res = sym_new_type(ctx, type); - } else { + } + else { res = sym_new_not_null(ctx); } } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 88cb3e51308bfb..56d40fe09d7d48 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -568,9 +568,11 @@ PyTypeObject *type = sym_get_type(container); if (type == &PyUnicode_Type || type == &PyList_Type || - type == &PyTuple_Type) { + type == &PyTuple_Type) + { res = sym_new_type(ctx, type); - } else { + } + else { res = sym_new_not_null(ctx); } stack_pointer[-3] = res;