From 9fc66f8b78a34d8839d6c1bd1a89dc10878b117e Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 9 Aug 2025 04:01:02 +0000 Subject: [PATCH 1/7] [mypyc] feat: reuse existing tuple when calling fn(*args) --- mypyc/irbuild/ll_builder.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index a5e28268efed..74e0ce4e0818 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -788,7 +788,17 @@ def _construct_varargs( for value, kind, name in args: if kind == ARG_STAR: if star_result is None: - star_result = self.new_list_op(star_values, line) + # fast path if star expr is a tuple: + # we can pass the immutable tuple straight into the function call. + if is_tuple_rprimitive(value.type) and ( + len(args) == 1 or (len(args) == 2 and args[1][1] == ARG_STAR2) + ): # only matches fn(*args) and fn(*args, **kwargs) + # TODO extend this to optimize fn(*args, k=1, **kwargs) case + star_result = value + continue + else: + # TODO optimize this case using the length utils - currently in review + star_result = self.new_list_op(star_values, line) self.primitive_op(list_extend_op, [star_result, value], line) elif kind == ARG_STAR2: if star2_result is None: @@ -885,9 +895,13 @@ def _construct_varargs( # tuple. Otherwise create the tuple from the list. if star_result is None: star_result = self.new_tuple(star_values, line) + elif is_tuple_rprimitive(star_result.type): + # We're just passing in the tuple + pass else: star_result = self.primitive_op(list_tuple_op, [star_result], line) if has_star2 and star2_result is None: + # TODO: use dict_copy_op for simple cases of **kwargs star2_result = self._create_dict(star2_keys, star2_values, line) return star_result, star2_result From 4db8e938331e78f671fe7f12247f35b8fe3e44e1 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 9 Aug 2025 04:19:38 +0000 Subject: [PATCH 2/7] chore: cleanup --- mypyc/irbuild/ll_builder.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 74e0ce4e0818..49fc82d28fc8 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -790,12 +790,15 @@ def _construct_varargs( if star_result is None: # fast path if star expr is a tuple: # we can pass the immutable tuple straight into the function call. - if is_tuple_rprimitive(value.type) and ( - len(args) == 1 or (len(args) == 2 and args[1][1] == ARG_STAR2) - ): # only matches fn(*args) and fn(*args, **kwargs) + if is_tuple_rprimitive(value.type): + if len(args) == 1: + # fn(*args) + return value, None + elif len(args) == 2 and args[1][1] == ARG_STAR2: + # fn(*args, **kwargs) + star_result = value + continue # TODO extend this to optimize fn(*args, k=1, **kwargs) case - star_result = value - continue else: # TODO optimize this case using the length utils - currently in review star_result = self.new_list_op(star_values, line) From d7094257dae29948ffe8983f09d51acac72af5d7 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 9 Aug 2025 04:22:22 +0000 Subject: [PATCH 3/7] chore: update IR --- mypyc/test-data/irbuild-generics.test | 58 +++++++++++---------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index d39d47e397a1..a4f427bf83c0 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -166,25 +166,18 @@ execute(f, 1) def execute(func, args, kwargs): func :: object args :: tuple - kwargs :: dict - r0 :: list - r1 :: object - r2 :: dict - r3 :: i32 - r4 :: bit - r5 :: tuple - r6 :: object - r7 :: int + kwargs, r0 :: dict + r1 :: i32 + r2 :: bit + r3 :: object + r4 :: int L0: - r0 = PyList_New(0) - r1 = CPyList_Extend(r0, args) - r2 = PyDict_New() - r3 = CPyDict_UpdateInDisplay(r2, kwargs) - r4 = r3 >= 0 :: signed - r5 = PyList_AsTuple(r0) - r6 = PyObject_Call(func, r5, r2) - r7 = unbox(int, r6) - return r7 + r0 = PyDict_New() + r1 = CPyDict_UpdateInDisplay(r0, kwargs) + r2 = r1 >= 0 :: signed + r3 = PyObject_Call(func, args, r0) + r4 = unbox(int, r3) + return r4 def f(x): x :: int L0: @@ -709,14 +702,11 @@ def inner_deco_obj.__call__(__mypyc_self__, args, kwargs): can_dictcomp :: dict r22, can_iter, r23, can_use_keys, r24, can_use_values :: list r25 :: object - r26 :: list - r27 :: object - r28 :: dict - r29 :: i32 - r30 :: bit - r31 :: tuple - r32 :: object - r33 :: int + r26 :: dict + r27 :: i32 + r28 :: bit + r29 :: object + r30 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = var_object_size args @@ -768,15 +758,12 @@ L9: r24 = CPyDict_Values(kwargs) can_use_values = r24 r25 = r0.func - r26 = PyList_New(0) - r27 = CPyList_Extend(r26, args) - r28 = PyDict_New() - r29 = CPyDict_UpdateInDisplay(r28, kwargs) - r30 = r29 >= 0 :: signed - r31 = PyList_AsTuple(r26) - r32 = PyObject_Call(r25, r31, r28) - r33 = unbox(int, r32) - return r33 + r26 = PyDict_New() + r27 = CPyDict_UpdateInDisplay(r26, kwargs) + r28 = r27 >= 0 :: signed + r29 = PyObject_Call(r25, args, r26) + r30 = unbox(int, r29) + return r30 def deco(func): func :: object r0 :: __main__.deco_env @@ -795,3 +782,4 @@ def f(x): x :: int L0: return x + From 301080ac6f0190d3193df10d2185c57293d1f81e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 04:23:45 +0000 Subject: [PATCH 4/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/test-data/irbuild-generics.test | 1 - 1 file changed, 1 deletion(-) diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index a4f427bf83c0..03032a7746c0 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -782,4 +782,3 @@ def f(x): x :: int L0: return x - From e028a7283107db3908a75be5b034941d1e7d22d5 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 9 Aug 2025 04:29:51 +0000 Subject: [PATCH 5/7] fix: mypy errs --- mypyc/irbuild/ll_builder.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 49fc82d28fc8..070ae6825cd8 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -798,10 +798,9 @@ def _construct_varargs( # fn(*args, **kwargs) star_result = value continue - # TODO extend this to optimize fn(*args, k=1, **kwargs) case - else: - # TODO optimize this case using the length utils - currently in review - star_result = self.new_list_op(star_values, line) + # elif ...: TODO extend this to optimize fn(*args, k=1, **kwargs) case + # TODO optimize this case using the length utils - currently in review + star_result = self.new_list_op(star_values, line) self.primitive_op(list_extend_op, [star_result, value], line) elif kind == ARG_STAR2: if star2_result is None: From 9057461ed631388f3f07215e54b1698e59cc1ee1 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 9 Aug 2025 04:38:54 +0000 Subject: [PATCH 6/7] fix: mypy errs --- mypyc/irbuild/ll_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 070ae6825cd8..3204bedbc9eb 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -793,7 +793,7 @@ def _construct_varargs( if is_tuple_rprimitive(value.type): if len(args) == 1: # fn(*args) - return value, None + return value, self._create_dict([], [], line) elif len(args) == 2 and args[1][1] == ARG_STAR2: # fn(*args, **kwargs) star_result = value From d3b3f0d74826f55eb088255ec46bc1b6c499d68b Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 9 Aug 2025 04:59:30 +0000 Subject: [PATCH 7/7] chore: refactor --- mypyc/irbuild/ll_builder.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 3204bedbc9eb..a4483c5c4ce9 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -897,10 +897,8 @@ def _construct_varargs( # tuple. Otherwise create the tuple from the list. if star_result is None: star_result = self.new_tuple(star_values, line) - elif is_tuple_rprimitive(star_result.type): - # We're just passing in the tuple - pass - else: + elif not is_tuple_rprimitive(star_result.type): + # if star_result is a tuple we took the fast path star_result = self.primitive_op(list_tuple_op, [star_result], line) if has_star2 and star2_result is None: # TODO: use dict_copy_op for simple cases of **kwargs