Skip to content

[mypyc] feat: extend stararg fastpath from #19629 with star2 fastpath #19630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Aug 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9fc66f8
[mypyc] feat: reuse existing tuple when calling fn(*args)
BobTheBuidler Aug 9, 2025
4db8e93
chore: cleanup
BobTheBuidler Aug 9, 2025
d709425
chore: update IR
BobTheBuidler Aug 9, 2025
301080a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 9, 2025
e028a72
fix: mypy errs
BobTheBuidler Aug 9, 2025
3315a97
Merge branch 'tupe' of https://github.com/BobTheBuidler/mypy into tupe
BobTheBuidler Aug 9, 2025
9057461
fix: mypy errs
BobTheBuidler Aug 9, 2025
d3b3f0d
chore: refactor
BobTheBuidler Aug 9, 2025
27a13a7
feat: extend stararg fastpath logic to handle lists and generic seque…
BobTheBuidler Aug 9, 2025
3e72105
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 9, 2025
3da4bf2
update IR
BobTheBuidler Aug 9, 2025
567b8a4
Merge branch 'stararg-fastpath' of https://github.com/BobTheBuidler/m…
BobTheBuidler Aug 9, 2025
2f0e0fe
fix mypy errs
BobTheBuidler Aug 9, 2025
82e39c4
[mypyc] feat: extend stararg fastpath from #19629 with star2 fastpath
BobTheBuidler Aug 9, 2025
aed4a8b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 9, 2025
2be0326
fix: separate fastpath for dict vs nondict
BobTheBuidler Aug 9, 2025
23536da
Merge branch 'star2arg-fastpath' of https://github.com/BobTheBuidler/…
BobTheBuidler Aug 9, 2025
847ea62
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 9, 2025
f260ada
Update ll_builder.py
BobTheBuidler Aug 9, 2025
f5f96df
Update ll_builder.py
BobTheBuidler Aug 9, 2025
e4af7e4
Merge branch 'master' into star2arg-fastpath
BobTheBuidler Aug 13, 2025
6b8b293
Merge branch 'master' into star2arg-fastpath
BobTheBuidler Aug 14, 2025
d70a024
Merge branch 'master' into star2arg-fastpath
BobTheBuidler Aug 18, 2025
16096d6
Update irbuild-basic.test
BobTheBuidler Aug 19, 2025
df7cd21
Update ll_builder.py
BobTheBuidler Aug 19, 2025
565a127
Update ll_builder.py
BobTheBuidler Aug 19, 2025
506c206
Merge branch 'master' into star2arg-fastpath
BobTheBuidler Aug 19, 2025
bf92a97
add run tests
BobTheBuidler Aug 20, 2025
8e2b6e9
Merge branch 'master' into star2arg-fastpath
BobTheBuidler Aug 20, 2025
680aa89
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 20, 2025
3b69d15
Update run-functions.test
BobTheBuidler Aug 20, 2025
b477f4d
Update run-functions.test
BobTheBuidler Aug 21, 2025
4f09215
Update run-functions.test
BobTheBuidler Aug 21, 2025
fd35224
Merge branch 'master' into star2arg-fastpath
BobTheBuidler Aug 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion mypyc/irbuild/ll_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@
from mypyc.primitives.bytes_ops import bytes_compare
from mypyc.primitives.dict_ops import (
dict_build_op,
dict_copy,
dict_copy_op,
dict_new_op,
dict_ssize_t_size_op,
dict_update_in_display_op,
Expand Down Expand Up @@ -806,19 +808,40 @@ def _construct_varargs(
return value, self._create_dict([], [], line)
elif len(args) == 2 and args[1][1] == ARG_STAR2:
# fn(*args, **kwargs)
# TODO: extend to cover(*args, **k, **w, **a, **r, **g, **s)
if is_tuple_rprimitive(value.type) or isinstance(value.type, RTuple):
star_result = value
elif is_list_rprimitive(value.type):
star_result = self.primitive_op(list_tuple_op, [value], line)
else:
star_result = self.primitive_op(sequence_tuple_op, [value], line)
continue

star2_arg = args[1]
star2_value = star2_arg[0]
if is_dict_rprimitive(star2_value.type):
star2_fastpath_op = dict_copy_op
else:
star2_fastpath_op = dict_copy
return star_result, self.primitive_op(
star2_fastpath_op, [star2_value], 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:
if len(args) == 1:
# early exit with fastpath if the only arg is ARG_STAR2
# TODO: can we maintain an empty tuple in memory and just reuse it again and again?
if is_dict_rprimitive(value.type):
star2_fastpath_op = dict_copy_op
else:
star2_fastpath_op = dict_copy
return self.new_tuple([], line), self.primitive_op(
star2_fastpath_op, [value], line
)

star2_result = self._create_dict(star2_keys, star2_values, line)

self.call_c(dict_update_in_display_op, [star2_result, value], line=line)
Expand Down
2 changes: 1 addition & 1 deletion mypyc/primitives/dict_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
)

# Construct a dictionary from another dictionary.
function_op(
dict_copy_op = function_op(
name="builtins.dict",
arg_types=[dict_rprimitive],
return_type=dict_rprimitive,
Expand Down
46 changes: 17 additions & 29 deletions mypyc/test-data/irbuild-basic.test
Original file line number Diff line number Diff line change
Expand Up @@ -1740,12 +1740,10 @@ def g():
r6, r7 :: dict
r8 :: str
r9 :: object
r10 :: dict
r11 :: i32
r12 :: bit
r13 :: tuple
r14 :: object
r15 :: tuple[int, int, int]
r10 :: tuple
r11 :: dict
r12 :: object
r13 :: tuple[int, int, int]
L0:
r0 = 'a'
r1 = 'b'
Expand All @@ -1757,13 +1755,11 @@ L0:
r7 = __main__.globals :: static
r8 = 'f'
r9 = CPyDict_GetItem(r7, r8)
r10 = PyDict_New()
r11 = CPyDict_UpdateInDisplay(r10, r6)
r12 = r11 >= 0 :: signed
r13 = PyTuple_Pack(0)
r14 = PyObject_Call(r9, r13, r10)
r15 = unbox(tuple[int, int, int], r14)
return r15
r10 = PyTuple_Pack(0)
r11 = PyDict_Copy(r6)
r12 = PyObject_Call(r9, r10, r11)
r13 = unbox(tuple[int, int, int], r12)
return r13
def h():
r0, r1 :: str
r2, r3 :: object
Expand Down Expand Up @@ -3670,18 +3666,14 @@ def wrapper_deco_obj.__call__(__mypyc_self__, lst, kwargs):
r1 :: object
r2 :: tuple
r3 :: dict
r4 :: i32
r5 :: bit
r6 :: object
r4 :: object
L0:
r0 = __mypyc_self__.__mypyc_env__
r1 = r0.fn
r2 = PyList_AsTuple(lst)
r3 = PyDict_New()
r4 = CPyDict_UpdateInDisplay(r3, kwargs)
r5 = r4 >= 0 :: signed
r6 = PyObject_Call(r1, r2, r3)
return r6
r3 = PyDict_Copy(kwargs)
r4 = PyObject_Call(r1, r2, r3)
return r4
def deco(fn):
fn :: object
r0 :: __main__.deco_env
Expand Down Expand Up @@ -3777,18 +3769,14 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args, kwargs):
r1 :: object
r2 :: tuple
r3 :: dict
r4 :: i32
r5 :: bit
r6 :: object
r4 :: object
L0:
r0 = __mypyc_self__.__mypyc_env__
r1 = r0.fn
r2 = PySequence_Tuple(args)
r3 = PyDict_New()
r4 = CPyDict_UpdateInDisplay(r3, kwargs)
r5 = r4 >= 0 :: signed
r6 = PyObject_Call(r1, r2, r3)
return r6
r3 = PyDict_Copy(kwargs)
r4 = PyObject_Call(r1, r2, r3)
return r4
def deco(fn):
fn :: object
r0 :: __main__.deco_env
Expand Down
32 changes: 12 additions & 20 deletions mypyc/test-data/irbuild-generics.test
Original file line number Diff line number Diff line change
Expand Up @@ -167,17 +167,13 @@ def execute(func, args, kwargs):
func :: object
args :: tuple
kwargs, r0 :: dict
r1 :: i32
r2 :: bit
r3 :: object
r4 :: int
r1 :: object
r2 :: int
L0:
r0 = PyDict_New()
r1 = CPyDict_UpdateInDisplay(r0, kwargs)
r2 = r1 >= 0 :: signed
r3 = PyObject_Call(func, args, r0)
r4 = unbox(int, r3)
return r4
r0 = PyDict_Copy(kwargs)
r1 = PyObject_Call(func, args, r0)
r2 = unbox(int, r1)
return r2
def f(x):
x :: int
L0:
Expand Down Expand Up @@ -703,10 +699,8 @@ def inner_deco_obj.__call__(__mypyc_self__, args, kwargs):
r22, can_iter, r23, can_use_keys, r24, can_use_values :: list
r25 :: object
r26 :: dict
r27 :: i32
r28 :: bit
r29 :: object
r30 :: int
r27 :: object
r28 :: int
L0:
r0 = __mypyc_self__.__mypyc_env__
r1 = var_object_size args
Expand Down Expand Up @@ -758,12 +752,10 @@ L9:
r24 = CPyDict_Values(kwargs)
can_use_values = r24
r25 = r0.func
r26 = PyDict_New()
r27 = CPyDict_UpdateInDisplay(r26, kwargs)
r28 = r27 >= 0 :: signed
r29 = PyObject_Call(r25, args, r26)
r30 = unbox(int, r29)
return r30
r26 = PyDict_Copy(kwargs)
r27 = PyObject_Call(r25, args, r26)
r28 = unbox(int, r27)
return r28
def deco(func):
func :: object
r0 :: __main__.deco_env
Expand Down
26 changes: 26 additions & 0 deletions mypyc/test-data/run-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -1312,3 +1312,29 @@ from native import f
print(f(1))
[out]
2

[case testStarArgFastPaths]
from typing import Any, Mapping
def fn(x: str, y: int) -> str:
return x * y
def star_tuple(*args: Any) -> str:
return fn(*args)
def star_list(args: list[Any]) -> str:
return fn(*args)
def star_generic(args: dict[Any, Any]) -> str:
return fn(*args)
def star2(**kwargs: Any) -> str:
return fn(**kwargs)
def star2_generic(kwargs: Mapping[Any, Any]) -> str:
return fn(**kwargs)

def test_star_fastpath_tuple() -> None:
assert star_tuple("a", 3) == "aaa"
def test_star_fastpath_list() -> None:
assert star_list(["a", 3]) == "aaa"
def test_star_fastpath_generic() -> None:
assert star_generic({"a": None, 3: None}) == "aaa"
def test_star2_fastpath() -> None:
assert star2(x="a", y=3) == "aaa"
def test_star2_fastpath_generic() -> None:
assert star2_generic({"x": "a", "y": 3}) == "aaa"
Loading