Skip to content

Commit d1dffe2

Browse files
[mypyc] feat: PyObject_CallObject op for fn(*args) fastpath (#19631)
This PR adds a new custom_op for PyObject_CallObject which is more efficient than PyObject_Call in cases where there are no kwargs. posarg-only use cases are already optimized but this is helpful for patterns such as `fn(*args)` or `fn(a1, a2, *args)` This PR extends #19623 and #19629 , as this change will not be helpful until those PRs are merged.
1 parent 50fc847 commit d1dffe2

File tree

4 files changed

+39
-38
lines changed

4 files changed

+39
-38
lines changed

mypyc/annotate.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def __init__(self, message: str, priority: int = 1) -> None:
7777
"PyNumber_Rshift": Annotation('Generic ">>" operation.'),
7878
"PyNumber_Invert": Annotation('Generic "~" operation.'),
7979
"PyObject_Call": Annotation("Generic call operation."),
80+
"PyObject_CallObject": Annotation("Generic call operation."),
8081
"PyObject_RichCompare": Annotation("Generic comparison operation."),
8182
"PyObject_GetItem": Annotation("Generic indexing operation."),
8283
"PyObject_SetItem": Annotation("Generic indexed assignment."),

mypyc/irbuild/ll_builder.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@
141141
generic_ssize_t_len_op,
142142
py_call_op,
143143
py_call_with_kwargs_op,
144+
py_call_with_posargs_op,
144145
py_getattr_op,
145146
py_method_call_op,
146147
py_vectorcall_method_op,
@@ -805,7 +806,7 @@ def _construct_varargs(
805806
value.type, RTuple
806807
):
807808
value = self.primitive_op(sequence_tuple_op, [value], line)
808-
return value, self._create_dict([], [], line)
809+
return value, None
809810
elif len(args) == 2 and args[1][1] == ARG_STAR2:
810811
# fn(*args, **kwargs)
811812
# TODO: extend to cover(*args, **k, **w, **a, **r, **g, **s)
@@ -938,7 +939,7 @@ def _construct_varargs(
938939
elif not is_tuple_rprimitive(star_result.type):
939940
# if star_result is a tuple we took the fast path
940941
star_result = self.primitive_op(list_tuple_op, [star_result], line)
941-
if has_star2 and star2_result is None:
942+
if has_star2 and star2_result is None and len(star2_keys) > 0:
942943
# TODO: use dict_copy_op for simple cases of **kwargs
943944
star2_result = self._create_dict(star2_keys, star2_values, line)
944945

@@ -964,13 +965,16 @@ def py_call(
964965
if arg_kinds is None or all(kind == ARG_POS for kind in arg_kinds):
965966
return self.call_c(py_call_op, [function] + arg_values, line)
966967

967-
# Otherwise fallback to py_call_with_kwargs_op.
968+
# Otherwise fallback to py_call_with_posargs_op or py_call_with_kwargs_op.
968969
assert arg_names is not None
969970

970971
pos_args_tuple, kw_args_dict = self._construct_varargs(
971972
list(zip(arg_values, arg_kinds, arg_names)), line, has_star=True, has_star2=True
972973
)
973-
assert pos_args_tuple and kw_args_dict
974+
assert pos_args_tuple
975+
976+
if kw_args_dict is None:
977+
return self.call_c(py_call_with_posargs_op, [function, pos_args_tuple], line)
974978

975979
return self.call_c(py_call_with_kwargs_op, [function, pos_args_tuple, kw_args_dict], line)
976980

@@ -1169,8 +1173,7 @@ def native_args_to_positional(
11691173
assert star_arg
11701174
output_arg = star_arg
11711175
elif arg.kind == ARG_STAR2:
1172-
assert star2_arg
1173-
output_arg = star2_arg
1176+
output_arg = star2_arg or self._create_dict([], [], line)
11741177
elif not lst:
11751178
if is_fixed_width_rtype(arg.type):
11761179
output_arg = Integer(0, arg.type)

mypyc/primitives/generic_ops.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,15 @@
308308
error_kind=ERR_MAGIC,
309309
)
310310

311+
# Call callable object with positional args only: func(*args)
312+
# Arguments are (func, *args tuple).
313+
py_call_with_posargs_op = custom_op(
314+
arg_types=[object_rprimitive, object_rprimitive],
315+
return_type=object_rprimitive,
316+
c_function_name="PyObject_CallObject",
317+
error_kind=ERR_MAGIC,
318+
)
319+
311320
# Call method with positional arguments: obj.method(arg1, ...)
312321
# Arguments are (object, attribute name, arg1, ...).
313322
py_method_call_op = custom_op(

mypyc/test-data/irbuild-basic.test

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,20 +1674,17 @@ def g():
16741674
r0 :: tuple[int, int, int]
16751675
r1 :: dict
16761676
r2 :: str
1677-
r3 :: object
1678-
r4 :: dict
1679-
r5, r6 :: object
1680-
r7 :: tuple[int, int, int]
1677+
r3, r4, r5 :: object
1678+
r6 :: tuple[int, int, int]
16811679
L0:
16821680
r0 = (2, 4, 6)
16831681
r1 = __main__.globals :: static
16841682
r2 = 'f'
16851683
r3 = CPyDict_GetItem(r1, r2)
1686-
r4 = PyDict_New()
1687-
r5 = box(tuple[int, int, int], r0)
1688-
r6 = PyObject_Call(r3, r5, r4)
1689-
r7 = unbox(tuple[int, int, int], r6)
1690-
return r7
1684+
r4 = box(tuple[int, int, int], r0)
1685+
r5 = PyObject_CallObject(r3, r4)
1686+
r6 = unbox(tuple[int, int, int], r5)
1687+
return r6
16911688
def h():
16921689
r0 :: tuple[int, int]
16931690
r1 :: dict
@@ -1698,9 +1695,8 @@ def h():
16981695
r6 :: ptr
16991696
r7, r8 :: object
17001697
r9 :: tuple
1701-
r10 :: dict
1702-
r11 :: object
1703-
r12 :: tuple[int, int, int]
1698+
r10 :: object
1699+
r11 :: tuple[int, int, int]
17041700
L0:
17051701
r0 = (4, 6)
17061702
r1 = __main__.globals :: static
@@ -1714,10 +1710,9 @@ L0:
17141710
r7 = box(tuple[int, int], r0)
17151711
r8 = CPyList_Extend(r4, r7)
17161712
r9 = PyList_AsTuple(r4)
1717-
r10 = PyDict_New()
1718-
r11 = PyObject_Call(r3, r9, r10)
1719-
r12 = unbox(tuple[int, int, int], r11)
1720-
return r12
1713+
r10 = PyObject_CallObject(r3, r9)
1714+
r11 = unbox(tuple[int, int, int], r10)
1715+
return r11
17211716

17221717
[case testStar2Args]
17231718
from typing import Tuple
@@ -3562,15 +3557,12 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args):
35623557
__mypyc_self__ :: __main__.wrapper_deco_obj
35633558
args :: tuple
35643559
r0 :: __main__.deco_env
3565-
r1 :: object
3566-
r2 :: dict
3567-
r3 :: object
3560+
r1, r2 :: object
35683561
L0:
35693562
r0 = __mypyc_self__.__mypyc_env__
35703563
r1 = r0.fn
3571-
r2 = PyDict_New()
3572-
r3 = PyObject_Call(r1, args, r2)
3573-
return r3
3564+
r2 = PyObject_CallObject(r1, args)
3565+
return r2
35743566
def deco(fn):
35753567
fn :: object
35763568
r0 :: __main__.deco_env
@@ -3613,15 +3605,13 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args):
36133605
r0 :: __main__.deco_env
36143606
r1 :: object
36153607
r2 :: tuple
3616-
r3 :: dict
3617-
r4 :: object
3608+
r3 :: object
36183609
L0:
36193610
r0 = __mypyc_self__.__mypyc_env__
36203611
r1 = r0.fn
36213612
r2 = PyList_AsTuple(args)
3622-
r3 = PyDict_New()
3623-
r4 = PyObject_Call(r1, r2, r3)
3624-
return r4
3613+
r3 = PyObject_CallObject(r1, r2)
3614+
return r3
36253615
def deco(fn):
36263616
fn :: object
36273617
r0 :: __main__.deco_env
@@ -3716,15 +3706,13 @@ def wrapper_deco_obj.__call__(__mypyc_self__, args):
37163706
r0 :: __main__.deco_env
37173707
r1 :: object
37183708
r2 :: tuple
3719-
r3 :: dict
3720-
r4 :: object
3709+
r3 :: object
37213710
L0:
37223711
r0 = __mypyc_self__.__mypyc_env__
37233712
r1 = r0.fn
37243713
r2 = PySequence_Tuple(args)
3725-
r3 = PyDict_New()
3726-
r4 = PyObject_Call(r1, r2, r3)
3727-
return r4
3714+
r3 = PyObject_CallObject(r1, r2)
3715+
return r3
37283716
def deco(fn):
37293717
fn :: object
37303718
r0 :: __main__.deco_env

0 commit comments

Comments
 (0)