From b63b142b324d2f09bf4221ec7dec7f2d46d94a6c Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:44:59 +0800 Subject: [PATCH 1/3] Type propagate _BINARY_OP_ADD_UNICODE --- Lib/test/test_capi/test_opt.py | 33 +++++++++++++++++-- .../tier2_redundancy_eliminator_bytecodes.c | 11 +++++++ Python/tier2_redundancy_eliminator_cases.c.h | 14 ++++++-- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 3ba38c77710b2b..4451748f44c5db 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -775,10 +775,13 @@ def testfunc(n): a = 1.0 for _ in range(n): a = a + 0.1 + a = a + 0.1 + a = a + 0.1 + a = a + 0.1 return a res, ex = self._run_with_optimizer(testfunc, 32) - self.assertAlmostEqual(res, 4.2) + self.assertAlmostEqual(res, 13.8) self.assertIsNotNone(ex) uops = {opname for opname, _, _ in ex} guard_both_float_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_FLOAT"] @@ -792,10 +795,13 @@ def testfunc(n): a = 1.0 for _ in range(n): a = a - 0.1 + a = a - 0.1 + a = a - 0.1 + a = a - 0.1 return a res, ex = self._run_with_optimizer(testfunc, 32) - self.assertAlmostEqual(res, -2.2) + self.assertAlmostEqual(res, -11.8) self.assertIsNotNone(ex) uops = {opname for opname, _, _ in ex} guard_both_float_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_FLOAT"] @@ -809,10 +815,13 @@ def testfunc(n): a = 1.0 for _ in range(n): a = a * 2.0 + a = a * 2.0 + a = a * 2.0 + a = a * 2.0 return a res, ex = self._run_with_optimizer(testfunc, 32) - self.assertAlmostEqual(res, 2 ** 32) + self.assertAlmostEqual(res, 2 ** (32 * 4)) self.assertIsNotNone(ex) uops = {opname for opname, _, _ in ex} guard_both_float_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_FLOAT"] @@ -821,6 +830,24 @@ def testfunc(n): # We'll also need to verify that propagation actually occurs. self.assertIn("_BINARY_OP_MULTIPLY_FLOAT", uops) + def test_add_unicode_propagation(self): + def testfunc(n): + a = "" + for _ in range(n): + a + a + a + a + a + a + a + a + return a + + res, ex = self._run_with_optimizer(testfunc, 32) + self.assertEqual(res, "") + self.assertIsNotNone(ex) + uops = {opname for opname, _, _ in ex} + guard_both_float_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_UNICODE"] + self.assertLessEqual(len(guard_both_float_count), 1) + self.assertIn("_BINARY_OP_ADD_UNICODE", uops) + def test_compare_op_type_propagation_float(self): def testfunc(n): a = 1.0 diff --git a/Python/tier2_redundancy_eliminator_bytecodes.c b/Python/tier2_redundancy_eliminator_bytecodes.c index e9b556d16c3702..4a2fcdf0ba1523 100644 --- a/Python/tier2_redundancy_eliminator_bytecodes.c +++ b/Python/tier2_redundancy_eliminator_bytecodes.c @@ -197,6 +197,17 @@ dummy_func(void) { } } + op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) { + if (is_const(left) && is_const(right)) { + PyObject *temp = PyUnicode_Concat(get_const(left), get_const(right)); + ERROR_IF(temp == NULL, error); + res = sym_new_const(ctx, temp); + } + else { + OUT_OF_SPACE_IF_NULL(res = sym_new_known_type(ctx, &PyUnicode_Type)); + } + } + op(_LOAD_CONST, (-- value)) { // There should be no LOAD_CONST. It should be all // replaced by peephole_opt. diff --git a/Python/tier2_redundancy_eliminator_cases.c.h b/Python/tier2_redundancy_eliminator_cases.c.h index f41fe328195b4d..e12e9e5b1eb4c1 100644 --- a/Python/tier2_redundancy_eliminator_cases.c.h +++ b/Python/tier2_redundancy_eliminator_cases.c.h @@ -365,9 +365,19 @@ } case _BINARY_OP_ADD_UNICODE: { + _Py_UOpsSymType *right; + _Py_UOpsSymType *left; _Py_UOpsSymType *res; - res = sym_new_unknown(ctx); - if (res == NULL) goto out_of_space; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + if (is_const(left) && is_const(right)) { + PyObject *temp = PyUnicode_Concat(get_const(left), get_const(right)); + ERROR_IF(temp == NULL, error); + res = sym_new_const(ctx, temp); + } + else { + OUT_OF_SPACE_IF_NULL(res = sym_new_known_type(ctx, &PyUnicode_Type)); + } stack_pointer[-2] = res; stack_pointer += -1; break; From 85f2b478abfd6344b7d316c6551a50c850cf0733 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 21 Feb 2024 11:57:20 +0800 Subject: [PATCH 2/3] forgot error checking --- Python/tier2_redundancy_eliminator_bytecodes.c | 2 +- Python/tier2_redundancy_eliminator_cases.c.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/tier2_redundancy_eliminator_bytecodes.c b/Python/tier2_redundancy_eliminator_bytecodes.c index 4a2fcdf0ba1523..97bf608f0a065a 100644 --- a/Python/tier2_redundancy_eliminator_bytecodes.c +++ b/Python/tier2_redundancy_eliminator_bytecodes.c @@ -201,7 +201,7 @@ dummy_func(void) { if (is_const(left) && is_const(right)) { PyObject *temp = PyUnicode_Concat(get_const(left), get_const(right)); ERROR_IF(temp == NULL, error); - res = sym_new_const(ctx, temp); + OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, temp)); } else { OUT_OF_SPACE_IF_NULL(res = sym_new_known_type(ctx, &PyUnicode_Type)); diff --git a/Python/tier2_redundancy_eliminator_cases.c.h b/Python/tier2_redundancy_eliminator_cases.c.h index e12e9e5b1eb4c1..8fbd3cec0c54ee 100644 --- a/Python/tier2_redundancy_eliminator_cases.c.h +++ b/Python/tier2_redundancy_eliminator_cases.c.h @@ -373,7 +373,7 @@ if (is_const(left) && is_const(right)) { PyObject *temp = PyUnicode_Concat(get_const(left), get_const(right)); ERROR_IF(temp == NULL, error); - res = sym_new_const(ctx, temp); + OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, temp)); } else { OUT_OF_SPACE_IF_NULL(res = sym_new_known_type(ctx, &PyUnicode_Type)); From 8ed62e999c88e4b63e838553f5372bdd700cf984 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 2 Mar 2024 01:18:56 +0800 Subject: [PATCH 3/3] port to new style --- Lib/test/test_capi/test_opt.py | 36 +++++++++++++++++----------------- Python/optimizer_bytecodes.c | 16 +++++++++++++++ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index b62331e95c9abc..a0a19225b79433 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -795,14 +795,14 @@ def test_float_add_constant_propagation(self): def testfunc(n): a = 1.0 for _ in range(n): - a = a + 0.1 - a = a + 0.1 - a = a + 0.1 - a = a + 0.1 + a = a + 0.25 + a = a + 0.25 + a = a + 0.25 + a = a + 0.25 return a res, ex = self._run_with_optimizer(testfunc, 32) - self.assertAlmostEqual(res, 13.8) + self.assertAlmostEqual(res, 33.0) self.assertIsNotNone(ex) uops = get_opnames(ex) guard_both_float_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_FLOAT"] @@ -815,14 +815,14 @@ def test_float_subtract_constant_propagation(self): def testfunc(n): a = 1.0 for _ in range(n): - a = a - 0.1 - a = a - 0.1 - a = a - 0.1 - a = a - 0.1 + a = a - 0.25 + a = a - 0.25 + a = a - 0.25 + a = a - 0.25 return a res, ex = self._run_with_optimizer(testfunc, 32) - self.assertAlmostEqual(res, -11.8) + self.assertAlmostEqual(res, -31.0) self.assertIsNotNone(ex) uops = get_opnames(ex) guard_both_float_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_FLOAT"] @@ -835,14 +835,14 @@ def test_float_multiply_constant_propagation(self): def testfunc(n): a = 1.0 for _ in range(n): - a = a * 2.0 - a = a * 2.0 - a = a * 2.0 - a = a * 2.0 + a = a * 1.0 + a = a * 1.0 + a = a * 1.0 + a = a * 1.0 return a res, ex = self._run_with_optimizer(testfunc, 32) - self.assertAlmostEqual(res, 2 ** (32 * 4)) + self.assertAlmostEqual(res, 1.0) self.assertIsNotNone(ex) uops = get_opnames(ex) guard_both_float_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_FLOAT"] @@ -864,9 +864,9 @@ def testfunc(n): res, ex = self._run_with_optimizer(testfunc, 32) self.assertEqual(res, "") self.assertIsNotNone(ex) - uops = {opname for opname, _, _ in ex} - guard_both_float_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_UNICODE"] - self.assertLessEqual(len(guard_both_float_count), 1) + uops = get_opnames(ex) + guard_both_unicode_count = [opname for opname in iter_opnames(ex) if opname == "_GUARD_BOTH_UNICODE"] + self.assertLessEqual(len(guard_both_unicode_count), 1) self.assertIn("_BINARY_OP_ADD_UNICODE", uops) def test_compare_op_type_propagation_float(self): diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 2b47381ec76db4..786d884fc5a1a8 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -254,6 +254,22 @@ dummy_func(void) { } } + op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) { + if (sym_is_const(left) && sym_is_const(right) && + sym_matches_type(left, &PyUnicode_Type) && sym_matches_type(right, &PyUnicode_Type)) { + PyObject *temp = PyUnicode_Concat(sym_get_const(left), sym_get_const(right)); + if (temp == NULL) { + goto error; + } + res = sym_new_const(ctx, temp); + Py_DECREF(temp); + OUT_OF_SPACE_IF_NULL(res); + } + else { + OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyUnicode_Type)); + } + } + op(_TO_BOOL, (value -- res)) { (void)value; res = sym_new_type(ctx, &PyBool_Type);