From 389f99f1450dc51416124c9d5d66e37be9df943c Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sun, 2 Mar 2025 20:36:30 +0100 Subject: [PATCH 01/24] remove tuple folding from ast_opt.c --- Python/ast_opt.c | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/Python/ast_opt.c b/Python/ast_opt.c index 4a191e919e412c..8ee322fdd15197 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -390,44 +390,6 @@ fold_binop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state) return 1; } -static PyObject* -make_const_tuple(asdl_expr_seq *elts) -{ - for (Py_ssize_t i = 0; i < asdl_seq_LEN(elts); i++) { - expr_ty e = (expr_ty)asdl_seq_GET(elts, i); - if (e->kind != Constant_kind) { - return NULL; - } - } - - PyObject *newval = PyTuple_New(asdl_seq_LEN(elts)); - if (newval == NULL) { - return NULL; - } - - for (Py_ssize_t i = 0; i < asdl_seq_LEN(elts); i++) { - expr_ty e = (expr_ty)asdl_seq_GET(elts, i); - PyObject *v = e->v.Constant.value; - PyTuple_SET_ITEM(newval, i, Py_NewRef(v)); - } - return newval; -} - -static int -fold_tuple(expr_ty node, PyArena *arena, _PyASTOptimizeState *state) -{ - if (state->syntax_check_only) { - return 1; - } - PyObject *newval; - - if (node->v.Tuple.ctx != Load) - return 1; - - newval = make_const_tuple(node->v.Tuple.elts); - return make_const(node, newval, arena); -} - static int astfold_mod(mod_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); static int astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); static int astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); @@ -620,7 +582,6 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) break; case Tuple_kind: CALL_SEQ(astfold_expr, expr, node_->v.Tuple.elts); - CALL(fold_tuple, expr_ty, node_); break; case Name_kind: if (state->syntax_check_only) { From c1e4f23e7f7c9ed7ead5309a8aa74905ad9dfca2 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sun, 2 Mar 2025 20:36:57 +0100 Subject: [PATCH 02/24] preserve constant tuple warnings --- Python/codegen.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Python/codegen.c b/Python/codegen.c index e1f647451f7002..44d23ca6d93014 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -1688,11 +1688,26 @@ codegen_typealias(compiler *c, stmt_ty s) return SUCCESS; } +static bool +is_const_tuple(asdl_expr_seq *elts) +{ + for (Py_ssize_t i = 0; i < asdl_seq_LEN(elts); i++) { + expr_ty e = (expr_ty)asdl_seq_GET(elts, i); + if (e->kind != Constant_kind) { + return false; + } + } + return true; +} + /* Return false if the expression is a constant value except named singletons. Return true otherwise. */ static bool check_is_arg(expr_ty e) { + if (e->kind == Tuple_kind) { + return !is_const_tuple(e->v.Tuple.elts); + } if (e->kind != Constant_kind) { return true; } From 7ffac41f65b01e6b55824be0c6b99fb3736639cb Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sun, 2 Mar 2025 20:37:34 +0100 Subject: [PATCH 03/24] implement constant intrinsic list to tuple folding --- Python/flowgraph.c | 95 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 8154129090b222..98f1a527bc2fdb 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1482,6 +1482,93 @@ fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, PyObject *const return instr_make_load_const(instr, const_tuple, consts, const_cache); } +/* Replace: + BUILD_LIST 0 + LOAD_CONST c1 + LIST_APPEND + LOAD_CONST c2 + LIST_APPEND + ... + LOAD_CONST cN + LIST_APPEND + CALL_INTRINSIC_1 INTRINSIC_LIST_TO_TUPLE + with: + LOAD_CONST (c1, c2, ... cN) +*/ +static int +fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, + PyObject *consts, PyObject *const_cache) +{ + assert(PyDict_CheckExact(const_cache)); + assert(PyList_CheckExact(consts)); + assert(i >= 0 && i < bb->b_iused); + cfg_instr *intrinsic = &bb->b_instr[i]; + assert( + intrinsic->i_opcode == CALL_INTRINSIC_1 && + intrinsic->i_oparg == INTRINSIC_LIST_TO_TUPLE + ); + + PyObject *list = PyList_New(0); + if (list == NULL) { + return ERROR; + } + + bool expect_append = true; + + for (int pos = i-1; pos >= 0; pos--) { + cfg_instr *instr = &bb->b_instr[pos]; + + if (instr->i_opcode == NOP) { + continue; + } + + if (instr->i_opcode == BUILD_LIST && instr->i_oparg == 0) { + if (!expect_append) { + /* Not a sequence start */ + goto exit; + } + /* Sequence start, we are done. */ + if (PyList_Reverse(list) < 0) { + goto error; + } + PyObject *newconst = PyList_AsTuple(list); + if (newconst == NULL) { + goto error; + } + Py_DECREF(list); + int nops = (int)PyTuple_Size(newconst) * 2 + 1; + nop_out(bb, i-1, nops); + return instr_make_load_const(intrinsic, newconst, consts, const_cache); + } + + if (expect_append) { + if (!(instr->i_opcode == LIST_APPEND && instr->i_oparg == 1)) { + goto exit; + } + } + else { + if (!loads_const(instr->i_opcode)) { + goto exit; + } + PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); + int r = PyList_Append(list, constant); + Py_DECREF(constant); + if (r < 0) { + goto error; + } + } + + expect_append = !expect_append; + } + +exit: + Py_DECREF(list); + return SUCCESS; +error: + Py_DECREF(list); + return ERROR; +} + #define MIN_CONST_SEQUENCE_SIZE 3 /* Optimize lists and sets for: @@ -2379,8 +2466,12 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) break; case CALL_INTRINSIC_1: // for _ in (*foo, *bar) -> for _ in [*foo, *bar] - if (oparg == INTRINSIC_LIST_TO_TUPLE && nextop == GET_ITER) { - INSTR_SET_OP0(inst, NOP); + if (oparg == INTRINSIC_LIST_TO_TUPLE) { + if (nextop == GET_ITER) { + INSTR_SET_OP0(inst, NOP); + } else { + fold_constant_intrinsic_list_to_tuple(bb, i, consts, const_cache); + } } else if (oparg == INTRINSIC_UNARY_POSITIVE) { RETURN_IF_ERROR(fold_const_unaryop(bb, i, consts, const_cache)); From 4eae42322756a16921569f68afd40bd10d7a9ae2 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sun, 2 Mar 2025 20:38:21 +0100 Subject: [PATCH 04/24] adjust tests --- Lib/test/test_ast/test_ast.py | 111 ---------------------------------- Lib/test/test_builtin.py | 12 ++-- Lib/test/test_compile.py | 4 +- Lib/test/test_opcache.py | 8 ++- 4 files changed, 14 insertions(+), 121 deletions(-) diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py index e63ddb7d1fecc4..1b108ceddb1c11 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -153,22 +153,6 @@ def test_optimization_levels__debug__(self): self.assertIsInstance(res.body[0].value, ast.Name) self.assertEqual(res.body[0].value.id, expected) - def test_optimization_levels_const_folding(self): - folded = ('Expr', (1, 0, 1, 6), ('Constant', (1, 0, 1, 6), (1, 2), None)) - not_folded = ('Expr', (1, 0, 1, 6), - ('Tuple', (1, 0, 1, 6), - [('Constant', (1, 1, 1, 2), 1, None), - ('Constant', (1, 4, 1, 5), 2, None)], ('Load',))) - - cases = [(-1, not_folded), (0, not_folded), (1, folded), (2, folded)] - for (optval, expected) in cases: - with self.subTest(optval=optval): - tree1 = ast.parse("(1, 2)", optimize=optval) - tree2 = ast.parse(ast.parse("(1, 2)"), optimize=optval) - for tree in [tree1, tree2]: - res = to_tuple(tree.body[0]) - self.assertEqual(res, expected) - def test_invalid_position_information(self): invalid_linenos = [ (10, 1), (-10, -11), (10, -11), (-5, -2), (-5, 1) @@ -3193,101 +3177,6 @@ def test_folding_format(self): self.assert_ast(code, non_optimized_target, optimized_target) - - def test_folding_tuple(self): - code = "(1,)" - - non_optimized_target = self.wrap_expr(ast.Tuple(elts=[ast.Constant(1)])) - optimized_target = self.wrap_expr(ast.Constant(value=(1,))) - - self.assert_ast(code, non_optimized_target, optimized_target) - - def test_folding_type_param_in_function_def(self): - code = "def foo[%s = (1, 2)](): pass" - - unoptimized_tuple = ast.Tuple(elts=[ast.Constant(1), ast.Constant(2)]) - unoptimized_type_params = [ - ("T", "T", ast.TypeVar), - ("**P", "P", ast.ParamSpec), - ("*Ts", "Ts", ast.TypeVarTuple), - ] - - for type, name, type_param in unoptimized_type_params: - result_code = code % type - optimized_target = self.wrap_statement( - ast.FunctionDef( - name='foo', - args=ast.arguments(), - body=[ast.Pass()], - type_params=[type_param(name=name, default_value=ast.Constant((1, 2)))] - ) - ) - non_optimized_target = self.wrap_statement( - ast.FunctionDef( - name='foo', - args=ast.arguments(), - body=[ast.Pass()], - type_params=[type_param(name=name, default_value=unoptimized_tuple)] - ) - ) - self.assert_ast(result_code, non_optimized_target, optimized_target) - - def test_folding_type_param_in_class_def(self): - code = "class foo[%s = (1, 2)]: pass" - - unoptimized_tuple = ast.Tuple(elts=[ast.Constant(1), ast.Constant(2)]) - unoptimized_type_params = [ - ("T", "T", ast.TypeVar), - ("**P", "P", ast.ParamSpec), - ("*Ts", "Ts", ast.TypeVarTuple), - ] - - for type, name, type_param in unoptimized_type_params: - result_code = code % type - optimized_target = self.wrap_statement( - ast.ClassDef( - name='foo', - body=[ast.Pass()], - type_params=[type_param(name=name, default_value=ast.Constant((1, 2)))] - ) - ) - non_optimized_target = self.wrap_statement( - ast.ClassDef( - name='foo', - body=[ast.Pass()], - type_params=[type_param(name=name, default_value=unoptimized_tuple)] - ) - ) - self.assert_ast(result_code, non_optimized_target, optimized_target) - - def test_folding_type_param_in_type_alias(self): - code = "type foo[%s = (1, 2)] = 1" - - unoptimized_tuple = ast.Tuple(elts=[ast.Constant(1), ast.Constant(2)]) - unoptimized_type_params = [ - ("T", "T", ast.TypeVar), - ("**P", "P", ast.ParamSpec), - ("*Ts", "Ts", ast.TypeVarTuple), - ] - - for type, name, type_param in unoptimized_type_params: - result_code = code % type - optimized_target = self.wrap_statement( - ast.TypeAlias( - name=ast.Name(id='foo', ctx=ast.Store()), - type_params=[type_param(name=name, default_value=ast.Constant((1, 2)))], - value=ast.Constant(value=1), - ) - ) - non_optimized_target = self.wrap_statement( - ast.TypeAlias( - name=ast.Name(id='foo', ctx=ast.Store()), - type_params=[type_param(name=name, default_value=unoptimized_tuple)], - value=ast.Constant(value=1), - ) - ) - self.assert_ast(result_code, non_optimized_target, optimized_target) - def test_folding_match_case_allowed_expressions(self): def get_match_case_values(node): result = [] diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 90998ffce6d4e1..0cc172c8b77237 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -554,7 +554,7 @@ def test_compile_async_generator(self): self.assertEqual(type(glob['ticker']()), AsyncGeneratorType) def test_compile_ast(self): - args = ("a*(1,2)", "f.py", "exec") + args = ("a*__debug__", "f.py", "exec") raw = compile(*args, flags = ast.PyCF_ONLY_AST).body[0] opt1 = compile(*args, flags = ast.PyCF_OPTIMIZED_AST).body[0] opt2 = compile(ast.parse(args[0]), *args[1:], flags = ast.PyCF_OPTIMIZED_AST).body[0] @@ -565,14 +565,14 @@ def test_compile_ast(self): self.assertIsInstance(tree.value.left, ast.Name) self.assertEqual(tree.value.left.id, 'a') - raw_right = raw.value.right # expect Tuple((1, 2)) - self.assertIsInstance(raw_right, ast.Tuple) - self.assertListEqual([elt.value for elt in raw_right.elts], [1, 2]) + raw_right = raw.value.right + self.assertIsInstance(raw_right, ast.Name) + self.assertEqual(raw_right.id, "__debug__") for opt in [opt1, opt2]: - opt_right = opt.value.right # expect Constant((1,2)) + opt_right = opt.value.right self.assertIsInstance(opt_right, ast.Constant) - self.assertEqual(opt_right.value, (1, 2)) + self.assertEqual(opt_right.value, True) def test_delattr(self): sys.spam = 1 diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index f5d757d8fc72c9..8089e8993dd24a 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -793,9 +793,9 @@ def check_same_constant(const): f1, f2 = lambda: "not a name", lambda: ("not a name",) f3 = lambda x: x in {("not a name",)} self.assertIs(f1.__code__.co_consts[0], - f2.__code__.co_consts[0][0]) + f2.__code__.co_consts[1][0]) self.assertIs(next(iter(f3.__code__.co_consts[1])), - f2.__code__.co_consts[0]) + f2.__code__.co_consts[1]) # {0} is converted to a constant frozenset({0}) by the peephole # optimizer diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index ce9c489f32cf7d..ac132465b227ad 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1666,7 +1666,8 @@ def to_bool_str(): def test_unpack_sequence(self): def unpack_sequence_two_tuple(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): - a, b = 1, 2 + t = 1, 2 + a, b = t self.assertEqual(a, 1) self.assertEqual(b, 2) @@ -1677,8 +1678,11 @@ def unpack_sequence_two_tuple(): def unpack_sequence_tuple(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): - a, = 1, + a, b, c, d = 1, 2, 3, 4 self.assertEqual(a, 1) + self.assertEqual(b, 2) + self.assertEqual(c, 3) + self.assertEqual(d, 4) unpack_sequence_tuple() self.assert_specialized(unpack_sequence_tuple, "UNPACK_SEQUENCE_TUPLE") From 3691e74010ffc9a56fbdcaee39d8d83ea38b5ea9 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sun, 2 Mar 2025 20:38:36 +0100 Subject: [PATCH 05/24] add peepholer tests --- Lib/test/test_peepholer.py | 67 +++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 4644641144bee5..79ef10b4bec8a5 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -154,7 +154,7 @@ def test_folding_of_tuples_of_constants(self): for line, elem in ( ('a = 1,2,3', (1, 2, 3)), ('("a","b","c")', ('a', 'b', 'c')), - ('a,b,c = 1,2,3', (1, 2, 3)), + ('a,b,c,d = 1,2,3,4', (1, 2, 3, 4)), ('(None, 1, None)', (None, 1, None)), ('((1, 2), 3, 4)', ((1, 2), 3, 4)), ): @@ -1349,6 +1349,71 @@ def test_fold_tuple_of_constants(self): ] self.cfg_optimization_test(same, same, consts=[]) + def test_fold_constant_intrinsic_list_to_tuple(self): + INTRINSIC_LIST_TO_TUPLE = 6 + + # long tuple + consts = 1000 + before = ( + [('BUILD_LIST', 0, 0)] + + [('LOAD_CONST', 0, 0), ('LIST_APPEND', 1, 0)] * consts + + [('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), ('RETURN_VALUE', None, 0)] + ) + after = [ + ('LOAD_CONST', 1, 0), + ('RETURN_VALUE', None, 0) + ] + result_const = tuple(["test"] * consts) + self.cfg_optimization_test(before, after, consts=["test"], expected_consts=["test", result_const]) + + # empty list + before = [ + ('BUILD_LIST', 0, 0), + ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), + ('RETURN_VALUE', None, 0) + ] + after = [ + ('LOAD_CONST', 0, 0), + ('RETURN_VALUE', None, 0) + ] + self.cfg_optimization_test(before, after, consts=[], expected_consts=[()]) + + # multiple BUILD_LIST 0: ([], 1, [], 2) + same = [ + ('BUILD_LIST', 0, 0), + ('BUILD_LIST', 0, 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 1, 0), + ('LIST_APPEND', 1, 0), + ('BUILD_LIST', 0, 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 2, 0), + ('LIST_APPEND', 1, 0), + ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), + ('RETURN_VALUE', None, 0) + ] + self.cfg_optimization_test(same, same, consts=[]) + + # nested folding: (1, 1+1, 3) + before = [ + ('BUILD_LIST', 0, 0), + ('LOAD_SMALL_INT', 1, 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 1, 0), + ('LOAD_SMALL_INT', 1, 0), + ('BINARY_OP', get_binop_argval('NB_ADD'), 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 3, 0), + ('LIST_APPEND', 1, 0), + ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), + ('RETURN_VALUE', None, 0) + ] + after = [ + ('LOAD_CONST', 0, 0), + ('RETURN_VALUE', None, 0) + ] + self.cfg_optimization_test(before, after, consts=[], expected_consts=[(1, 2, 3)]) + def test_optimize_if_const_list(self): before = [ ('NOP', None, 0), From ed312ad853c1ac8cd7b39efe84017c2699694420 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sun, 2 Mar 2025 20:47:31 +0100 Subject: [PATCH 06/24] add constant null check --- Python/flowgraph.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 98f1a527bc2fdb..a0cd945926665e 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1551,6 +1551,9 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, goto exit; } PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); + if (constant == NULL) { + goto error; + } int r = PyList_Append(list, constant); Py_DECREF(constant); if (r < 0) { From 3e41b41c570febb7e8e4b08006e560c40b3beda2 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Mon, 3 Mar 2025 09:59:43 +0100 Subject: [PATCH 07/24] address review --- Python/flowgraph.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index a0cd945926665e..3021f8c0e6b33e 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1501,12 +1501,11 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, { assert(PyDict_CheckExact(const_cache)); assert(PyList_CheckExact(consts)); - assert(i >= 0 && i < bb->b_iused); + assert(i >= 0); + assert(i < bb->b_iused); cfg_instr *intrinsic = &bb->b_instr[i]; - assert( - intrinsic->i_opcode == CALL_INTRINSIC_1 && - intrinsic->i_oparg == INTRINSIC_LIST_TO_TUPLE - ); + assert(intrinsic->i_opcode == CALL_INTRINSIC_1); + assert(intrinsic->i_oparg == INTRINSIC_LIST_TO_TUPLE); PyObject *list = PyList_New(0); if (list == NULL) { @@ -2468,11 +2467,12 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) RETURN_IF_ERROR(fold_const_unaryop(bb, i, consts, const_cache)); break; case CALL_INTRINSIC_1: - // for _ in (*foo, *bar) -> for _ in [*foo, *bar] if (oparg == INTRINSIC_LIST_TO_TUPLE) { + // for _ in (*foo, *bar) -> for _ in [*foo, *bar] if (nextop == GET_ITER) { INSTR_SET_OP0(inst, NOP); - } else { + } + else { fold_constant_intrinsic_list_to_tuple(bb, i, consts, const_cache); } } From f150c15c473ea2996526d22d019eec07efbfbc8e Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Mon, 3 Mar 2025 10:02:33 +0100 Subject: [PATCH 08/24] update comment --- Python/flowgraph.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 3021f8c0e6b33e..b3e6a8380dc532 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1485,12 +1485,12 @@ fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, PyObject *const /* Replace: BUILD_LIST 0 LOAD_CONST c1 - LIST_APPEND + LIST_APPEND 1 LOAD_CONST c2 - LIST_APPEND + LIST_APPEND 1 ... LOAD_CONST cN - LIST_APPEND + LIST_APPEND 1 CALL_INTRINSIC_1 INTRINSIC_LIST_TO_TUPLE with: LOAD_CONST (c1, c2, ... cN) From 4331a08bd2b12d62bf10c49fbdb66764bdb01e29 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Mon, 3 Mar 2025 19:27:41 +0100 Subject: [PATCH 09/24] fold in codegen instead of cfg --- Lib/test/test_peepholer.py | 65 --------------------------- Python/codegen.c | 19 ++++++++ Python/flowgraph.c | 92 -------------------------------------- 3 files changed, 19 insertions(+), 157 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 79ef10b4bec8a5..5facbbde2210e7 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1349,71 +1349,6 @@ def test_fold_tuple_of_constants(self): ] self.cfg_optimization_test(same, same, consts=[]) - def test_fold_constant_intrinsic_list_to_tuple(self): - INTRINSIC_LIST_TO_TUPLE = 6 - - # long tuple - consts = 1000 - before = ( - [('BUILD_LIST', 0, 0)] + - [('LOAD_CONST', 0, 0), ('LIST_APPEND', 1, 0)] * consts + - [('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), ('RETURN_VALUE', None, 0)] - ) - after = [ - ('LOAD_CONST', 1, 0), - ('RETURN_VALUE', None, 0) - ] - result_const = tuple(["test"] * consts) - self.cfg_optimization_test(before, after, consts=["test"], expected_consts=["test", result_const]) - - # empty list - before = [ - ('BUILD_LIST', 0, 0), - ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), - ('RETURN_VALUE', None, 0) - ] - after = [ - ('LOAD_CONST', 0, 0), - ('RETURN_VALUE', None, 0) - ] - self.cfg_optimization_test(before, after, consts=[], expected_consts=[()]) - - # multiple BUILD_LIST 0: ([], 1, [], 2) - same = [ - ('BUILD_LIST', 0, 0), - ('BUILD_LIST', 0, 0), - ('LIST_APPEND', 1, 0), - ('LOAD_SMALL_INT', 1, 0), - ('LIST_APPEND', 1, 0), - ('BUILD_LIST', 0, 0), - ('LIST_APPEND', 1, 0), - ('LOAD_SMALL_INT', 2, 0), - ('LIST_APPEND', 1, 0), - ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), - ('RETURN_VALUE', None, 0) - ] - self.cfg_optimization_test(same, same, consts=[]) - - # nested folding: (1, 1+1, 3) - before = [ - ('BUILD_LIST', 0, 0), - ('LOAD_SMALL_INT', 1, 0), - ('LIST_APPEND', 1, 0), - ('LOAD_SMALL_INT', 1, 0), - ('LOAD_SMALL_INT', 1, 0), - ('BINARY_OP', get_binop_argval('NB_ADD'), 0), - ('LIST_APPEND', 1, 0), - ('LOAD_SMALL_INT', 3, 0), - ('LIST_APPEND', 1, 0), - ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), - ('RETURN_VALUE', None, 0) - ] - after = [ - ('LOAD_CONST', 0, 0), - ('RETURN_VALUE', None, 0) - ] - self.cfg_optimization_test(before, after, consts=[], expected_consts=[(1, 2, 3)]) - def test_optimize_if_const_list(self): before = [ ('NOP', None, 0), diff --git a/Python/codegen.c b/Python/codegen.c index 44d23ca6d93014..7c18ea09698e4f 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3246,6 +3246,25 @@ starunpack_helper_impl(compiler *c, location loc, } int sequence_built = 0; if (big) { + if (tuple) { + PyObject *newconst = PyTuple_New(n); + if (newconst == NULL) { + return ERROR; + } + bool is_const = true; + for (Py_ssize_t i = 0; i < n; i++) { + expr_ty elt = asdl_seq_GET(elts, i); + if (elt->kind != Constant_kind) { + is_const = false; + Py_DECREF(newconst); + break; + } + PyTuple_SET_ITEM(newconst, i, Py_NewRef(elt->v.Constant.value)); + } + if (is_const) { + return codegen_addop_load_const(c, loc, newconst); + } + } ADDOP_I(c, loc, build, pushed); sequence_built = 1; } diff --git a/Python/flowgraph.c b/Python/flowgraph.c index b3e6a8380dc532..8d1af323cd3c48 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1482,95 +1482,6 @@ fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, PyObject *const return instr_make_load_const(instr, const_tuple, consts, const_cache); } -/* Replace: - BUILD_LIST 0 - LOAD_CONST c1 - LIST_APPEND 1 - LOAD_CONST c2 - LIST_APPEND 1 - ... - LOAD_CONST cN - LIST_APPEND 1 - CALL_INTRINSIC_1 INTRINSIC_LIST_TO_TUPLE - with: - LOAD_CONST (c1, c2, ... cN) -*/ -static int -fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, - PyObject *consts, PyObject *const_cache) -{ - assert(PyDict_CheckExact(const_cache)); - assert(PyList_CheckExact(consts)); - assert(i >= 0); - assert(i < bb->b_iused); - cfg_instr *intrinsic = &bb->b_instr[i]; - assert(intrinsic->i_opcode == CALL_INTRINSIC_1); - assert(intrinsic->i_oparg == INTRINSIC_LIST_TO_TUPLE); - - PyObject *list = PyList_New(0); - if (list == NULL) { - return ERROR; - } - - bool expect_append = true; - - for (int pos = i-1; pos >= 0; pos--) { - cfg_instr *instr = &bb->b_instr[pos]; - - if (instr->i_opcode == NOP) { - continue; - } - - if (instr->i_opcode == BUILD_LIST && instr->i_oparg == 0) { - if (!expect_append) { - /* Not a sequence start */ - goto exit; - } - /* Sequence start, we are done. */ - if (PyList_Reverse(list) < 0) { - goto error; - } - PyObject *newconst = PyList_AsTuple(list); - if (newconst == NULL) { - goto error; - } - Py_DECREF(list); - int nops = (int)PyTuple_Size(newconst) * 2 + 1; - nop_out(bb, i-1, nops); - return instr_make_load_const(intrinsic, newconst, consts, const_cache); - } - - if (expect_append) { - if (!(instr->i_opcode == LIST_APPEND && instr->i_oparg == 1)) { - goto exit; - } - } - else { - if (!loads_const(instr->i_opcode)) { - goto exit; - } - PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); - if (constant == NULL) { - goto error; - } - int r = PyList_Append(list, constant); - Py_DECREF(constant); - if (r < 0) { - goto error; - } - } - - expect_append = !expect_append; - } - -exit: - Py_DECREF(list); - return SUCCESS; -error: - Py_DECREF(list); - return ERROR; -} - #define MIN_CONST_SEQUENCE_SIZE 3 /* Optimize lists and sets for: @@ -2472,9 +2383,6 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) if (nextop == GET_ITER) { INSTR_SET_OP0(inst, NOP); } - else { - fold_constant_intrinsic_list_to_tuple(bb, i, consts, const_cache); - } } else if (oparg == INTRINSIC_UNARY_POSITIVE) { RETURN_IF_ERROR(fold_const_unaryop(bb, i, consts, const_cache)); From 70c4388a15713fc2c8f0fc058ac7339178bc3ab0 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Wed, 5 Mar 2025 13:31:32 +0100 Subject: [PATCH 10/24] update const tuple creation --- Python/codegen.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/Python/codegen.c b/Python/codegen.c index 7c18ea09698e4f..132fca912b57c0 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3221,11 +3221,15 @@ starunpack_helper_impl(compiler *c, location loc, Py_ssize_t n = asdl_seq_LEN(elts); int big = n + pushed + (injected_arg ? 1 : 0) > _PY_STACK_USE_GUIDELINE; int seen_star = 0; + int is_const = 1; for (Py_ssize_t i = 0; i < n; i++) { expr_ty elt = asdl_seq_GET(elts, i); - if (elt->kind == Starred_kind) { - seen_star = 1; - break; + if (elt->kind != Constant_kind) { + is_const = 0; + if (elt->kind == Starred_kind) { + seen_star = 1; + break; + } } } if (!seen_star && !big) { @@ -3246,24 +3250,18 @@ starunpack_helper_impl(compiler *c, location loc, } int sequence_built = 0; if (big) { - if (tuple) { + if (is_const && tuple && !injected_arg && !pushed) { PyObject *newconst = PyTuple_New(n); if (newconst == NULL) { return ERROR; } - bool is_const = true; for (Py_ssize_t i = 0; i < n; i++) { expr_ty elt = asdl_seq_GET(elts, i); - if (elt->kind != Constant_kind) { - is_const = false; - Py_DECREF(newconst); - break; - } PyTuple_SET_ITEM(newconst, i, Py_NewRef(elt->v.Constant.value)); } - if (is_const) { - return codegen_addop_load_const(c, loc, newconst); - } + int r = codegen_addop_load_const(c, loc, newconst); + Py_DECREF(newconst); + return r; } ADDOP_I(c, loc, build, pushed); sequence_built = 1; From 4a44c02e57f04fae70dcdda34d24a52c37e6a1e6 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Wed, 5 Mar 2025 20:57:24 +0100 Subject: [PATCH 11/24] Revert "update const tuple creation" This reverts commit dfad19824e3373df55ed8c456b88bf023398b53f. --- Python/codegen.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Python/codegen.c b/Python/codegen.c index 132fca912b57c0..7c18ea09698e4f 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3221,15 +3221,11 @@ starunpack_helper_impl(compiler *c, location loc, Py_ssize_t n = asdl_seq_LEN(elts); int big = n + pushed + (injected_arg ? 1 : 0) > _PY_STACK_USE_GUIDELINE; int seen_star = 0; - int is_const = 1; for (Py_ssize_t i = 0; i < n; i++) { expr_ty elt = asdl_seq_GET(elts, i); - if (elt->kind != Constant_kind) { - is_const = 0; - if (elt->kind == Starred_kind) { - seen_star = 1; - break; - } + if (elt->kind == Starred_kind) { + seen_star = 1; + break; } } if (!seen_star && !big) { @@ -3250,18 +3246,24 @@ starunpack_helper_impl(compiler *c, location loc, } int sequence_built = 0; if (big) { - if (is_const && tuple && !injected_arg && !pushed) { + if (tuple) { PyObject *newconst = PyTuple_New(n); if (newconst == NULL) { return ERROR; } + bool is_const = true; for (Py_ssize_t i = 0; i < n; i++) { expr_ty elt = asdl_seq_GET(elts, i); + if (elt->kind != Constant_kind) { + is_const = false; + Py_DECREF(newconst); + break; + } PyTuple_SET_ITEM(newconst, i, Py_NewRef(elt->v.Constant.value)); } - int r = codegen_addop_load_const(c, loc, newconst); - Py_DECREF(newconst); - return r; + if (is_const) { + return codegen_addop_load_const(c, loc, newconst); + } } ADDOP_I(c, loc, build, pushed); sequence_built = 1; From d0cd2a5d0098927b81afb34a1b9df3a7f4f5d8af Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Wed, 5 Mar 2025 20:57:39 +0100 Subject: [PATCH 12/24] Revert "fold in codegen instead of cfg" This reverts commit 70ce2c81da3e4d64a87ef043c9bdd6d982fb66a8. --- Lib/test/test_peepholer.py | 65 +++++++++++++++++++++++++++ Python/codegen.c | 19 -------- Python/flowgraph.c | 92 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 5facbbde2210e7..79ef10b4bec8a5 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1349,6 +1349,71 @@ def test_fold_tuple_of_constants(self): ] self.cfg_optimization_test(same, same, consts=[]) + def test_fold_constant_intrinsic_list_to_tuple(self): + INTRINSIC_LIST_TO_TUPLE = 6 + + # long tuple + consts = 1000 + before = ( + [('BUILD_LIST', 0, 0)] + + [('LOAD_CONST', 0, 0), ('LIST_APPEND', 1, 0)] * consts + + [('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), ('RETURN_VALUE', None, 0)] + ) + after = [ + ('LOAD_CONST', 1, 0), + ('RETURN_VALUE', None, 0) + ] + result_const = tuple(["test"] * consts) + self.cfg_optimization_test(before, after, consts=["test"], expected_consts=["test", result_const]) + + # empty list + before = [ + ('BUILD_LIST', 0, 0), + ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), + ('RETURN_VALUE', None, 0) + ] + after = [ + ('LOAD_CONST', 0, 0), + ('RETURN_VALUE', None, 0) + ] + self.cfg_optimization_test(before, after, consts=[], expected_consts=[()]) + + # multiple BUILD_LIST 0: ([], 1, [], 2) + same = [ + ('BUILD_LIST', 0, 0), + ('BUILD_LIST', 0, 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 1, 0), + ('LIST_APPEND', 1, 0), + ('BUILD_LIST', 0, 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 2, 0), + ('LIST_APPEND', 1, 0), + ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), + ('RETURN_VALUE', None, 0) + ] + self.cfg_optimization_test(same, same, consts=[]) + + # nested folding: (1, 1+1, 3) + before = [ + ('BUILD_LIST', 0, 0), + ('LOAD_SMALL_INT', 1, 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 1, 0), + ('LOAD_SMALL_INT', 1, 0), + ('BINARY_OP', get_binop_argval('NB_ADD'), 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 3, 0), + ('LIST_APPEND', 1, 0), + ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), + ('RETURN_VALUE', None, 0) + ] + after = [ + ('LOAD_CONST', 0, 0), + ('RETURN_VALUE', None, 0) + ] + self.cfg_optimization_test(before, after, consts=[], expected_consts=[(1, 2, 3)]) + def test_optimize_if_const_list(self): before = [ ('NOP', None, 0), diff --git a/Python/codegen.c b/Python/codegen.c index 7c18ea09698e4f..44d23ca6d93014 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3246,25 +3246,6 @@ starunpack_helper_impl(compiler *c, location loc, } int sequence_built = 0; if (big) { - if (tuple) { - PyObject *newconst = PyTuple_New(n); - if (newconst == NULL) { - return ERROR; - } - bool is_const = true; - for (Py_ssize_t i = 0; i < n; i++) { - expr_ty elt = asdl_seq_GET(elts, i); - if (elt->kind != Constant_kind) { - is_const = false; - Py_DECREF(newconst); - break; - } - PyTuple_SET_ITEM(newconst, i, Py_NewRef(elt->v.Constant.value)); - } - if (is_const) { - return codegen_addop_load_const(c, loc, newconst); - } - } ADDOP_I(c, loc, build, pushed); sequence_built = 1; } diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 8d1af323cd3c48..b3e6a8380dc532 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1482,6 +1482,95 @@ fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, PyObject *const return instr_make_load_const(instr, const_tuple, consts, const_cache); } +/* Replace: + BUILD_LIST 0 + LOAD_CONST c1 + LIST_APPEND 1 + LOAD_CONST c2 + LIST_APPEND 1 + ... + LOAD_CONST cN + LIST_APPEND 1 + CALL_INTRINSIC_1 INTRINSIC_LIST_TO_TUPLE + with: + LOAD_CONST (c1, c2, ... cN) +*/ +static int +fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, + PyObject *consts, PyObject *const_cache) +{ + assert(PyDict_CheckExact(const_cache)); + assert(PyList_CheckExact(consts)); + assert(i >= 0); + assert(i < bb->b_iused); + cfg_instr *intrinsic = &bb->b_instr[i]; + assert(intrinsic->i_opcode == CALL_INTRINSIC_1); + assert(intrinsic->i_oparg == INTRINSIC_LIST_TO_TUPLE); + + PyObject *list = PyList_New(0); + if (list == NULL) { + return ERROR; + } + + bool expect_append = true; + + for (int pos = i-1; pos >= 0; pos--) { + cfg_instr *instr = &bb->b_instr[pos]; + + if (instr->i_opcode == NOP) { + continue; + } + + if (instr->i_opcode == BUILD_LIST && instr->i_oparg == 0) { + if (!expect_append) { + /* Not a sequence start */ + goto exit; + } + /* Sequence start, we are done. */ + if (PyList_Reverse(list) < 0) { + goto error; + } + PyObject *newconst = PyList_AsTuple(list); + if (newconst == NULL) { + goto error; + } + Py_DECREF(list); + int nops = (int)PyTuple_Size(newconst) * 2 + 1; + nop_out(bb, i-1, nops); + return instr_make_load_const(intrinsic, newconst, consts, const_cache); + } + + if (expect_append) { + if (!(instr->i_opcode == LIST_APPEND && instr->i_oparg == 1)) { + goto exit; + } + } + else { + if (!loads_const(instr->i_opcode)) { + goto exit; + } + PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); + if (constant == NULL) { + goto error; + } + int r = PyList_Append(list, constant); + Py_DECREF(constant); + if (r < 0) { + goto error; + } + } + + expect_append = !expect_append; + } + +exit: + Py_DECREF(list); + return SUCCESS; +error: + Py_DECREF(list); + return ERROR; +} + #define MIN_CONST_SEQUENCE_SIZE 3 /* Optimize lists and sets for: @@ -2383,6 +2472,9 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) if (nextop == GET_ITER) { INSTR_SET_OP0(inst, NOP); } + else { + fold_constant_intrinsic_list_to_tuple(bb, i, consts, const_cache); + } } else if (oparg == INTRINSIC_UNARY_POSITIVE) { RETURN_IF_ERROR(fold_const_unaryop(bb, i, consts, const_cache)); From e4d916401dd13360269eca6638f7a91ef8fc4206 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Wed, 5 Mar 2025 21:22:26 +0100 Subject: [PATCH 13/24] remove comment --- Python/flowgraph.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index b3e6a8380dc532..c9dfe9ea7429ee 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -2468,7 +2468,6 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) break; case CALL_INTRINSIC_1: if (oparg == INTRINSIC_LIST_TO_TUPLE) { - // for _ in (*foo, *bar) -> for _ in [*foo, *bar] if (nextop == GET_ITER) { INSTR_SET_OP0(inst, NOP); } From 75a2762434265fbad6f92dff464aecc30944c1dc Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Wed, 5 Mar 2025 21:28:02 +0100 Subject: [PATCH 14/24] replace typeparam tests from test_ast in test_compile --- Lib/test/test_compile.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 8089e8993dd24a..ac583500bd88a6 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1129,6 +1129,31 @@ def foo(x): self.assertIn('LOAD_ATTR', instructions) self.assertIn('CALL', instructions) + def test_folding_type_param(self): + get_code_fn_cls = lambda x: x.co_consts[0].co_consts[2] + get_code_type_alias = lambda x: x.co_consts[0].co_consts[3] + snippets = [ + ("def foo[T = 40 + 5](): pass", get_code_fn_cls), + ("def foo[**P = 40 + 5](): pass", get_code_fn_cls), + ("def foo[*Ts = 40 + 5](): pass", get_code_fn_cls), + ("class foo[T = 40 + 5]: pass", get_code_fn_cls), + ("class foo[**P = 40 + 5]: pass", get_code_fn_cls), + ("class foo[*Ts = 40 + 5]: pass", get_code_fn_cls), + ("type foo[T = 40 + 5] = 1", get_code_type_alias), + ("type foo[**P = 40 + 5] = 1", get_code_type_alias), + ("type foo[*Ts = 40 + 5] = 1", get_code_type_alias), + ] + for snippet, get_code in snippets: + c = compile(snippet, "", "exec") + code = get_code(c) + opcodes = list(dis.get_instructions(code)) + instructions = [opcode.opname for opcode in opcodes] + args = [opcode.oparg for opcode in opcodes] + self.assertNotIn(40, args) + self.assertNotIn(5, args) + self.assertIn('LOAD_SMALL_INT', instructions) + self.assertIn(45, args) + def test_lineno_procedure_call(self): def call(): ( From f0429c8c1ba4f30da88836bc2c58b043e5036ace Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Thu, 6 Mar 2025 09:27:09 +0100 Subject: [PATCH 15/24] process 2 instructions per iteration --- Python/flowgraph.c | 47 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index c9dfe9ea7429ee..29bb8acff0ee19 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1512,8 +1512,6 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, return ERROR; } - bool expect_append = true; - for (int pos = i-1; pos >= 0; pos--) { cfg_instr *instr = &bb->b_instr[pos]; @@ -1522,10 +1520,6 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, } if (instr->i_opcode == BUILD_LIST && instr->i_oparg == 0) { - if (!expect_append) { - /* Not a sequence start */ - goto exit; - } /* Sequence start, we are done. */ if (PyList_Reverse(list) < 0) { goto error; @@ -1540,27 +1534,30 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, return instr_make_load_const(intrinsic, newconst, consts, const_cache); } - if (expect_append) { - if (!(instr->i_opcode == LIST_APPEND && instr->i_oparg == 1)) { - goto exit; - } + if (pos < 1) { + /* Can't process 2 instructions. */ + goto exit; } - else { - if (!loads_const(instr->i_opcode)) { - goto exit; - } - PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); - if (constant == NULL) { - goto error; - } - int r = PyList_Append(list, constant); - Py_DECREF(constant); - if (r < 0) { - goto error; - } + + if (!(instr->i_opcode == LIST_APPEND && instr->i_oparg == 1)) { + goto exit; + } + + instr = &bb->b_instr[--pos]; + if (!loads_const(instr->i_opcode)) { + goto exit; } - expect_append = !expect_append; + PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); + if (constant == NULL) { + goto error; + } + + int r = PyList_Append(list, constant); + Py_DECREF(constant); + if (r < 0) { + goto error; + } } exit: @@ -2472,7 +2469,7 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) INSTR_SET_OP0(inst, NOP); } else { - fold_constant_intrinsic_list_to_tuple(bb, i, consts, const_cache); + RETURN_IF_ERROR(fold_constant_intrinsic_list_to_tuple(bb, i, consts, const_cache)); } } else if (oparg == INTRINSIC_UNARY_POSITIVE) { From 939fbeea33ec88f86dbadc53141192db980b3959 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Thu, 6 Mar 2025 15:23:05 +0100 Subject: [PATCH 16/24] make 2 passes to avoid unnecessary allocations --- Python/flowgraph.c | 75 ++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 29bb8acff0ee19..a904c7d11417ca 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1507,65 +1507,62 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, assert(intrinsic->i_opcode == CALL_INTRINSIC_1); assert(intrinsic->i_oparg == INTRINSIC_LIST_TO_TUPLE); - PyObject *list = PyList_New(0); - if (list == NULL) { - return ERROR; - } + int consts_found = 0; + bool start_found = false; + bool expect_append = true; for (int pos = i-1; pos >= 0; pos--) { cfg_instr *instr = &bb->b_instr[pos]; - if (instr->i_opcode == NOP) { continue; } - if (instr->i_opcode == BUILD_LIST && instr->i_oparg == 0) { - /* Sequence start, we are done. */ - if (PyList_Reverse(list) < 0) { - goto error; + if (instr->i_opcode == BUILD_LIST &&instr->i_oparg == 0) { + start_found = expect_append; + break; + } + + if (expect_append) { + if (!(instr->i_opcode == LIST_APPEND && instr->i_oparg == 1)) { + break; } - PyObject *newconst = PyList_AsTuple(list); - if (newconst == NULL) { - goto error; + } + else { + if (!loads_const(instr->i_opcode)) { + break; } - Py_DECREF(list); - int nops = (int)PyTuple_Size(newconst) * 2 + 1; - nop_out(bb, i-1, nops); - return instr_make_load_const(intrinsic, newconst, consts, const_cache); + consts_found++; } - if (pos < 1) { - /* Can't process 2 instructions. */ - goto exit; - } + expect_append = !expect_append; + } - if (!(instr->i_opcode == LIST_APPEND && instr->i_oparg == 1)) { - goto exit; - } + if (!start_found) { + return SUCCESS; + } - instr = &bb->b_instr[--pos]; + PyObject *newconst = PyTuple_New((Py_ssize_t)consts_found); + if (newconst == NULL) { + return ERROR; + } + + int nops = consts_found * 2 + 1; + for (int pos = i-1; pos >= 0 && consts_found > 0; pos--) { + cfg_instr *instr = &bb->b_instr[pos]; if (!loads_const(instr->i_opcode)) { - goto exit; + continue; } - PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); if (constant == NULL) { - goto error; - } - - int r = PyList_Append(list, constant); - Py_DECREF(constant); - if (r < 0) { - goto error; + Py_DECREF(newconst); + return ERROR; } + PyTuple_SET_ITEM(newconst, --consts_found, constant); } -exit: - Py_DECREF(list); - return SUCCESS; -error: - Py_DECREF(list); - return ERROR; + assert(consts_found == 0); + nop_out(bb, i-1, nops); + return instr_make_load_const(intrinsic, newconst, consts, const_cache); } #define MIN_CONST_SEQUENCE_SIZE 3 From 57e6f075dc39adfe8caaa69c910d3f966c8faf6d Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Thu, 6 Mar 2025 15:23:20 +0100 Subject: [PATCH 17/24] add more tests --- Lib/test/test_peepholer.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 79ef10b4bec8a5..2cfaeaa2c6f6bc 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1414,6 +1414,33 @@ def test_fold_constant_intrinsic_list_to_tuple(self): ] self.cfg_optimization_test(before, after, consts=[], expected_consts=[(1, 2, 3)]) + # NOP's in between: (1, 2, 3) + before = [ + ('BUILD_LIST', 0, 0), + ('NOP', None, 0), + ('LOAD_SMALL_INT', 1, 0), + ('NOP', None, 0), + ('NOP', None, 0), + ('LIST_APPEND', 1, 0), + ('NOP', None, 0), + ('LOAD_SMALL_INT', 2, 0), + ('NOP', None, 0), + ('NOP', None, 0), + ('LIST_APPEND', 1, 0), + ('NOP', None, 0), + ('LOAD_SMALL_INT', 3, 0), + ('NOP', None, 0), + ('LIST_APPEND', 1, 0), + ('NOP', None, 0), + ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), + ('RETURN_VALUE', None, 0) + ] + after = [ + ('LOAD_CONST', 0, 0), + ('RETURN_VALUE', None, 0) + ] + self.cfg_optimization_test(before, after, consts=[], expected_consts=[(1, 2, 3)]) + def test_optimize_if_const_list(self): before = [ ('NOP', None, 0), From 4ae59f86ece7c7496381780ef45abba6eef9d7ca Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Thu, 6 Mar 2025 21:35:36 +0100 Subject: [PATCH 18/24] improve styling --- Python/flowgraph.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index a904c7d11417ca..2ec24157e9ef5a 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1511,24 +1511,27 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, bool start_found = false; bool expect_append = true; - for (int pos = i-1; pos >= 0; pos--) { + for (int pos = i - 1; pos >= 0; pos--) { cfg_instr *instr = &bb->b_instr[pos]; - if (instr->i_opcode == NOP) { + int opcode = instr->i_opcode; + int oparg = instr->i_oparg; + + if (opcode == NOP) { continue; } - if (instr->i_opcode == BUILD_LIST &&instr->i_oparg == 0) { + if (opcode == BUILD_LIST && oparg == 0) { start_found = expect_append; break; } if (expect_append) { - if (!(instr->i_opcode == LIST_APPEND && instr->i_oparg == 1)) { + if (opcode != LIST_APPEND || oparg != 1) { break; } } else { - if (!loads_const(instr->i_opcode)) { + if (!loads_const(opcode)) { break; } consts_found++; @@ -1547,17 +1550,16 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, } int nops = consts_found * 2 + 1; - for (int pos = i-1; pos >= 0 && consts_found > 0; pos--) { + for (int pos = i - 1; pos >= 0 && consts_found > 0; pos--) { cfg_instr *instr = &bb->b_instr[pos]; - if (!loads_const(instr->i_opcode)) { - continue; - } - PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); - if (constant == NULL) { - Py_DECREF(newconst); - return ERROR; + if (loads_const(instr->i_opcode)) { + PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); + if (constant == NULL) { + Py_DECREF(newconst); + return ERROR; + } + PyTuple_SET_ITEM(newconst, --consts_found, constant); } - PyTuple_SET_ITEM(newconst, --consts_found, constant); } assert(consts_found == 0); From bffc456184d95d5697d1b4c97696f228343c74a5 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Fri, 7 Mar 2025 09:00:54 +0100 Subject: [PATCH 19/24] address reivew --- Python/flowgraph.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 2ec24157e9ef5a..fa21214e5558e0 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1503,12 +1503,12 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, assert(PyList_CheckExact(consts)); assert(i >= 0); assert(i < bb->b_iused); + cfg_instr *intrinsic = &bb->b_instr[i]; assert(intrinsic->i_opcode == CALL_INTRINSIC_1); assert(intrinsic->i_oparg == INTRINSIC_LIST_TO_TUPLE); int consts_found = 0; - bool start_found = false; bool expect_append = true; for (int pos = i - 1; pos >= 0; pos--) { @@ -1521,18 +1521,20 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, } if (opcode == BUILD_LIST && oparg == 0) { - start_found = expect_append; - break; + if (!expect_append) { + return SUCCESS; + } + goto start_found; } if (expect_append) { if (opcode != LIST_APPEND || oparg != 1) { - break; + return SUCCESS; } } else { if (!loads_const(opcode)) { - break; + return SUCCESS; } consts_found++; } @@ -1540,10 +1542,10 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, expect_append = !expect_append; } - if (!start_found) { - return SUCCESS; - } + /* Did not find sequence start. */ + return SUCCESS; +start_found:; PyObject *newconst = PyTuple_New((Py_ssize_t)consts_found); if (newconst == NULL) { return ERROR; From e8e70e0594c65620fe0c790f2cec5e0a8d8d8236 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Fri, 7 Mar 2025 11:19:48 +0100 Subject: [PATCH 20/24] get rid of label --- Python/flowgraph.c | 48 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index fa21214e5558e0..77063c952cce83 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1522,9 +1522,32 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, if (opcode == BUILD_LIST && oparg == 0) { if (!expect_append) { + /* Not a sequence start. */ return SUCCESS; } - goto start_found; + + /* Sequence start, we are done. */ + PyObject *newconst = PyTuple_New((Py_ssize_t)consts_found); + if (newconst == NULL) { + return ERROR; + } + + int nops = consts_found * 2 + 1; + for (pos = i - 1; pos >= 0 && consts_found > 0; pos--) { + instr = &bb->b_instr[pos]; + if (loads_const(instr->i_opcode)) { + PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); + if (constant == NULL) { + Py_DECREF(newconst); + return ERROR; + } + PyTuple_SET_ITEM(newconst, --consts_found, constant); + } + } + + assert(consts_found == 0); + nop_out(bb, i-1, nops); + return instr_make_load_const(intrinsic, newconst, consts, const_cache); } if (expect_append) { @@ -1544,29 +1567,6 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, /* Did not find sequence start. */ return SUCCESS; - -start_found:; - PyObject *newconst = PyTuple_New((Py_ssize_t)consts_found); - if (newconst == NULL) { - return ERROR; - } - - int nops = consts_found * 2 + 1; - for (int pos = i - 1; pos >= 0 && consts_found > 0; pos--) { - cfg_instr *instr = &bb->b_instr[pos]; - if (loads_const(instr->i_opcode)) { - PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); - if (constant == NULL) { - Py_DECREF(newconst); - return ERROR; - } - PyTuple_SET_ITEM(newconst, --consts_found, constant); - } - } - - assert(consts_found == 0); - nop_out(bb, i-1, nops); - return instr_make_load_const(intrinsic, newconst, consts, const_cache); } #define MIN_CONST_SEQUENCE_SIZE 3 From a7dbe98f9024ebdcbccfda3df0d35c86a666523c Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Fri, 7 Mar 2025 13:06:37 +0100 Subject: [PATCH 21/24] inline noping out --- Python/flowgraph.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 77063c952cce83..4fee2a5be56bb5 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1532,21 +1532,24 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, return ERROR; } - int nops = consts_found * 2 + 1; - for (pos = i - 1; pos >= 0 && consts_found > 0; pos--) { - instr = &bb->b_instr[pos]; + for (int newpos = i - 1; newpos >= pos; newpos--) { + instr = &bb->b_instr[newpos]; + if (instr->i_opcode == NOP) { + continue; + } if (loads_const(instr->i_opcode)) { PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); if (constant == NULL) { Py_DECREF(newconst); return ERROR; } + assert(consts_found > 0); PyTuple_SET_ITEM(newconst, --consts_found, constant); } + INSTR_SET_OP0(instr, NOP); + INSTR_SET_LOC(instr, NO_LOCATION); } - assert(consts_found == 0); - nop_out(bb, i-1, nops); return instr_make_load_const(intrinsic, newconst, consts, const_cache); } From 836c0ee500d58c1e6509320756a66f62690b03a5 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Fri, 7 Mar 2025 13:06:48 +0100 Subject: [PATCH 22/24] add more tests --- Lib/test/test_peepholer.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 2cfaeaa2c6f6bc..6de89c4043d3f7 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1441,6 +1441,19 @@ def test_fold_constant_intrinsic_list_to_tuple(self): ] self.cfg_optimization_test(before, after, consts=[], expected_consts=[(1, 2, 3)]) + # no sequence start + same = [ + ('LOAD_SMALL_INT', 1, 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 2, 0), + ('LIST_APPEND', 1, 0), + ('LOAD_SMALL_INT', 3, 0), + ('LIST_APPEND', 1, 0), + ('CALL_INTRINSIC_1', INTRINSIC_LIST_TO_TUPLE, 0), + ('RETURN_VALUE', None, 0) + ] + self.cfg_optimization_test(same, same, consts=[]) + def test_optimize_if_const_list(self): before = [ ('NOP', None, 0), From 72a3e652f8f8863874ecc4713a0c06a7dea4a1aa Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Tue, 18 Mar 2025 13:34:18 +0100 Subject: [PATCH 23/24] resolve conflicts --- Programs/test_frozenmain.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index 1c7f74cc8039f2..281cf1c1bf9b27 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -9,19 +9,19 @@ unsigned char M_test_frozenmain[] = { 30,0,89,1,78,8,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,32,0,50,0,0,0,0,0, 0,0,80,4,43,26,0,0,0,0,0,0,0,0,0,0, - 112,5,80,5,15,0,68,24,0,0,112,6,89,2,32,0, - 80,6,89,6,11,0,80,7,89,5,89,6,43,26,0,0, + 112,5,80,7,15,0,68,24,0,0,112,6,89,2,32,0, + 80,5,89,6,11,0,80,6,89,5,89,6,43,26,0,0, 0,0,0,0,0,0,0,0,11,0,48,4,50,1,0,0, 0,0,0,0,30,0,73,26,0,0,8,0,29,0,80,1, 34,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122, 101,110,32,72,101,108,108,111,32,87,111,114,108,100,122,8, 115,121,115,46,97,114,103,118,218,6,99,111,110,102,105,103, - 41,5,218,12,112,114,111,103,114,97,109,95,110,97,109,101, - 218,10,101,120,101,99,117,116,97,98,108,101,218,15,117,115, - 101,95,101,110,118,105,114,111,110,109,101,110,116,218,17,99, - 111,110,102,105,103,117,114,101,95,99,95,115,116,100,105,111, - 218,14,98,117,102,102,101,114,101,100,95,115,116,100,105,111, - 122,7,99,111,110,102,105,103,32,122,2,58,32,41,7,218, + 122,7,99,111,110,102,105,103,32,122,2,58,32,41,5,218, + 12,112,114,111,103,114,97,109,95,110,97,109,101,218,10,101, + 120,101,99,117,116,97,98,108,101,218,15,117,115,101,95,101, + 110,118,105,114,111,110,109,101,110,116,218,17,99,111,110,102, + 105,103,117,114,101,95,99,95,115,116,100,105,111,218,14,98, + 117,102,102,101,114,101,100,95,115,116,100,105,111,41,7,218, 3,115,121,115,218,17,95,116,101,115,116,105,110,116,101,114, 110,97,108,99,97,112,105,218,5,112,114,105,110,116,218,4, 97,114,103,118,218,11,103,101,116,95,99,111,110,102,105,103, From 967a18e368ee4788f1c32ea7f3aa1ae77c078ca6 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Tue, 18 Mar 2025 14:32:48 +0100 Subject: [PATCH 24/24] use new nop_out --- Python/flowgraph.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 4fee2a5be56bb5..cf3e74005ce1f8 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1546,8 +1546,7 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, assert(consts_found > 0); PyTuple_SET_ITEM(newconst, --consts_found, constant); } - INSTR_SET_OP0(instr, NOP); - INSTR_SET_LOC(instr, NO_LOCATION); + nop_out(&instr, 1); } assert(consts_found == 0); return instr_make_load_const(intrinsic, newconst, consts, const_cache); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy