Skip to content

Commit 27a13a7

Browse files
committed
feat: extend stararg fastpath logic to handle lists and generic sequences
1 parent d3b3f0d commit 27a13a7

File tree

3 files changed

+281
-9
lines changed

3 files changed

+281
-9
lines changed

mypyc/irbuild/ll_builder.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@
183183
str_ssize_t_size_op,
184184
unicode_compare,
185185
)
186-
from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op, new_tuple_with_length_op
186+
from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op, new_tuple_with_length_op, sequence_tuple_op
187187
from mypyc.rt_subtype import is_runtime_subtype
188188
from mypyc.sametype import is_same_type
189189
from mypyc.subtype import is_subtype
@@ -790,14 +790,22 @@ def _construct_varargs(
790790
if star_result is None:
791791
# fast path if star expr is a tuple:
792792
# we can pass the immutable tuple straight into the function call.
793-
if is_tuple_rprimitive(value.type):
794-
if len(args) == 1:
795-
# fn(*args)
796-
return value, self._create_dict([], [], line)
797-
elif len(args) == 2 and args[1][1] == ARG_STAR2:
798-
# fn(*args, **kwargs)
793+
if len(args) == 1:
794+
# fn(*args)
795+
if is_list_rprimitive(value.type):
796+
value = self.call_c(list_tuple_op, [value], line)
797+
elif not is_tuple_rprimitive(value.type):
798+
value = self.call_c(sequence_tuple_op, [value], line)
799+
return value, self._create_dict([], [], line)
800+
elif len(args) == 2 and args[1][1] == ARG_STAR2:
801+
# fn(*args, **kwargs)
802+
if is_tuple_rprimitive(value.type):
799803
star_result = value
800-
continue
804+
elif is_list_rprimitive(value.type):
805+
star_result = self.call_c(list_tuple_op, [value], line)
806+
else:
807+
star_result = self.call_c(sequence_tuple_op, [value], line)
808+
continue
801809
# elif ...: TODO extend this to optimize fn(*args, k=1, **kwargs) case
802810
# TODO optimize this case using the length utils - currently in review
803811
star_result = self.new_list_op(star_values, line)

mypyc/primitives/tuple_ops.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
)
7777

7878
# Construct tuple from an arbitrary (iterable) object.
79-
function_op(
79+
sequence_tuple_op = function_op(
8080
name="builtins.tuple",
8181
arg_types=[object_rprimitive],
8282
return_type=tuple_rprimitive,

mypyc/test-data/irbuild-basic.test

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3546,3 +3546,267 @@ L0:
35463546
r2 = PyObject_Vectorcall(r1, 0, 0, 0)
35473547
r3 = box(None, 1)
35483548
return r3
3549+
3550+
[case testStarArgFastPathTuple]
3551+
from typing import Any, Callable
3552+
def deco(fn: Callable[..., Any]) -> Callable[..., Any]:
3553+
def wrapper(*args: Any) -> Any:
3554+
return fn(*args)
3555+
return wrapper
3556+
3557+
[out]
3558+
def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
3559+
__mypyc_self__, instance, owner, r0 :: object
3560+
r1 :: bit
3561+
r2 :: object
3562+
L0:
3563+
r0 = load_address _Py_NoneStruct
3564+
r1 = instance == r0
3565+
if r1 goto L1 else goto L2 :: bool
3566+
L1:
3567+
return __mypyc_self__
3568+
L2:
3569+
r2 = PyMethod_New(__mypyc_self__, instance)
3570+
return r2
3571+
def wrapper_deco_obj.__call__(__mypyc_self__, args):
3572+
__mypyc_self__ :: __main__.wrapper_deco_obj
3573+
args :: tuple
3574+
r0 :: __main__.deco_env
3575+
r1 :: object
3576+
r2 :: dict
3577+
r3 :: object
3578+
L0:
3579+
r0 = __mypyc_self__.__mypyc_env__
3580+
r1 = r0.fn
3581+
r2 = PyDict_New()
3582+
r3 = PyObject_Call(r1, args, r2)
3583+
return r3
3584+
def deco(fn):
3585+
fn :: object
3586+
r0 :: __main__.deco_env
3587+
r1 :: bool
3588+
r2 :: __main__.wrapper_deco_obj
3589+
r3 :: bool
3590+
wrapper :: object
3591+
L0:
3592+
r0 = deco_env()
3593+
r0.fn = fn; r1 = is_error
3594+
r2 = wrapper_deco_obj()
3595+
r2.__mypyc_env__ = r0; r3 = is_error
3596+
wrapper = r2
3597+
return wrapper
3598+
3599+
[case testStarArgFastPathList]
3600+
from typing import Any, Callable, List
3601+
def deco(fn: Callable[..., Any]) -> Callable[[List[Any]], Any]:
3602+
def wrapper(args: List[Any]) -> Any:
3603+
return fn(*args)
3604+
return wrapper
3605+
3606+
[out]
3607+
def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
3608+
__mypyc_self__, instance, owner, r0 :: object
3609+
r1 :: bit
3610+
r2 :: object
3611+
L0:
3612+
r0 = load_address _Py_NoneStruct
3613+
r1 = instance == r0
3614+
if r1 goto L1 else goto L2 :: bool
3615+
L1:
3616+
return __mypyc_self__
3617+
L2:
3618+
r2 = PyMethod_New(__mypyc_self__, instance)
3619+
return r2
3620+
def wrapper_deco_obj.__call__(__mypyc_self__, args):
3621+
__mypyc_self__ :: __main__.wrapper_deco_obj
3622+
args :: list
3623+
r0 :: __main__.deco_env
3624+
r1 :: object
3625+
r2 :: tuple
3626+
r3 :: dict
3627+
r4 :: object
3628+
L0:
3629+
r0 = __mypyc_self__.__mypyc_env__
3630+
r1 = r0.fn
3631+
r2 = PyList_AsTuple(args)
3632+
r3 = PyDict_New()
3633+
r4 = PyObject_Call(r1, r2, r3)
3634+
return r4
3635+
def deco(fn):
3636+
fn :: object
3637+
r0 :: __main__.deco_env
3638+
r1 :: bool
3639+
r2 :: __main__.wrapper_deco_obj
3640+
r3 :: bool
3641+
wrapper :: object
3642+
L0:
3643+
r0 = deco_env()
3644+
r0.fn = fn; r1 = is_error
3645+
r2 = wrapper_deco_obj()
3646+
r2.__mypyc_env__ = r0; r3 = is_error
3647+
wrapper = r2
3648+
return wrapper
3649+
3650+
[case testStarArgFastPathListWithKwargs]
3651+
from typing import Any, Callable, Dict, List
3652+
def deco(fn: Callable[..., Any]) -> Callable[..., Any]:
3653+
def wrapper(lst: List[Any], kwargs: Dict[str, Any]) -> Any:
3654+
return fn(*lst, **kwargs)
3655+
return wrapper
3656+
3657+
[out]
3658+
def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
3659+
__mypyc_self__, instance, owner, r0 :: object
3660+
r1 :: bit
3661+
r2 :: object
3662+
L0:
3663+
r0 = load_address _Py_NoneStruct
3664+
r1 = instance == r0
3665+
if r1 goto L1 else goto L2 :: bool
3666+
L1:
3667+
return __mypyc_self__
3668+
L2:
3669+
r2 = PyMethod_New(__mypyc_self__, instance)
3670+
return r2
3671+
def wrapper_deco_obj.__call__(__mypyc_self__, lst, kwargs):
3672+
__mypyc_self__ :: __main__.wrapper_deco_obj
3673+
lst :: list
3674+
kwargs :: dict
3675+
r0 :: __main__.deco_env
3676+
r1 :: object
3677+
r2 :: tuple
3678+
r3 :: dict
3679+
r4 :: i32
3680+
r5 :: bit
3681+
r6 :: object
3682+
L0:
3683+
r0 = __mypyc_self__.__mypyc_env__
3684+
r1 = r0.fn
3685+
r2 = PyList_AsTuple(lst)
3686+
r3 = PyDict_New()
3687+
r4 = CPyDict_UpdateInDisplay(r3, kwargs)
3688+
r5 = r4 >= 0 :: signed
3689+
r6 = PyObject_Call(r1, r2, r3)
3690+
return r6
3691+
def deco(fn):
3692+
fn :: object
3693+
r0 :: __main__.deco_env
3694+
r1 :: bool
3695+
r2 :: __main__.wrapper_deco_obj
3696+
r3 :: bool
3697+
wrapper :: object
3698+
L0:
3699+
r0 = deco_env()
3700+
r0.fn = fn; r1 = is_error
3701+
r2 = wrapper_deco_obj()
3702+
r2.__mypyc_env__ = r0; r3 = is_error
3703+
wrapper = r2
3704+
return wrapper
3705+
3706+
[case testStarArgFastPathSequence]
3707+
from typing import Any, Callable
3708+
def deco(fn: Callable[[Any], Any]) -> Callable[[Any], Any]:
3709+
def wrapper(args: Any) -> Any:
3710+
return fn(*args)
3711+
return wrapper
3712+
3713+
[out]
3714+
def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
3715+
__mypyc_self__, instance, owner, r0 :: object
3716+
r1 :: bit
3717+
r2 :: object
3718+
L0:
3719+
r0 = load_address _Py_NoneStruct
3720+
r1 = instance == r0
3721+
if r1 goto L1 else goto L2 :: bool
3722+
L1:
3723+
return __mypyc_self__
3724+
L2:
3725+
r2 = PyMethod_New(__mypyc_self__, instance)
3726+
return r2
3727+
def wrapper_deco_obj.__call__(__mypyc_self__, args):
3728+
__mypyc_self__ :: __main__.wrapper_deco_obj
3729+
args :: object
3730+
r0 :: __main__.deco_env
3731+
r1 :: object
3732+
r2 :: tuple
3733+
r3 :: dict
3734+
r4 :: object
3735+
L0:
3736+
r0 = __mypyc_self__.__mypyc_env__
3737+
r1 = r0.fn
3738+
r2 = PySequence_Tuple(args)
3739+
r3 = PyDict_New()
3740+
r4 = PyObject_Call(r1, r2, r3)
3741+
return r4
3742+
def deco(fn):
3743+
fn :: object
3744+
r0 :: __main__.deco_env
3745+
r1 :: bool
3746+
r2 :: __main__.wrapper_deco_obj
3747+
r3 :: bool
3748+
wrapper :: object
3749+
L0:
3750+
r0 = deco_env()
3751+
r0.fn = fn; r1 = is_error
3752+
r2 = wrapper_deco_obj()
3753+
r2.__mypyc_env__ = r0; r3 = is_error
3754+
wrapper = r2
3755+
return wrapper
3756+
3757+
[case testStarArgFastPathSequenceWithKwargs]
3758+
from typing import Any, Callable
3759+
def deco(fn: Callable[[Any], Any]) -> Callable[[Any], Any]:
3760+
def wrapper(args: Any, **kwargs: Any) -> Any:
3761+
return fn(*args, **kwargs)
3762+
return wrapper
3763+
3764+
[out]
3765+
def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner):
3766+
__mypyc_self__, instance, owner, r0 :: object
3767+
r1 :: bit
3768+
r2 :: object
3769+
L0:
3770+
r0 = load_address _Py_NoneStruct
3771+
r1 = instance == r0
3772+
if r1 goto L1 else goto L2 :: bool
3773+
L1:
3774+
return __mypyc_self__
3775+
L2:
3776+
r2 = PyMethod_New(__mypyc_self__, instance)
3777+
return r2
3778+
def wrapper_deco_obj.__call__(__mypyc_self__, args, kwargs):
3779+
__mypyc_self__ :: __main__.wrapper_deco_obj
3780+
args :: object
3781+
kwargs :: dict
3782+
r0 :: __main__.deco_env
3783+
r1 :: object
3784+
r2 :: tuple
3785+
r3 :: dict
3786+
r4 :: i32
3787+
r5 :: bit
3788+
r6 :: object
3789+
L0:
3790+
r0 = __mypyc_self__.__mypyc_env__
3791+
r1 = r0.fn
3792+
r2 = PySequence_Tuple(args)
3793+
r3 = PyDict_New()
3794+
r4 = CPyDict_UpdateInDisplay(r3, kwargs)
3795+
r5 = r4 >= 0 :: signed
3796+
r6 = PyObject_Call(r1, r2, r3)
3797+
return r6
3798+
def deco(fn):
3799+
fn :: object
3800+
r0 :: __main__.deco_env
3801+
r1 :: bool
3802+
r2 :: __main__.wrapper_deco_obj
3803+
r3 :: bool
3804+
wrapper :: object
3805+
L0:
3806+
r0 = deco_env()
3807+
r0.fn = fn; r1 = is_error
3808+
r2 = wrapper_deco_obj()
3809+
r2.__mypyc_env__ = r0; r3 = is_error
3810+
wrapper = r2
3811+
return wrapper
3812+

0 commit comments

Comments
 (0)