Skip to content

Commit eae1860

Browse files
authored
[mypyc] Support argument reordering in CallC (#9067)
Related: mypyc/mypyc#734. Support argument reordering via adding a new field to the description. It will solve the difference in args ordering between python syntax and C calling requirement (mostly with in ops). It should never be used together with variable args.
1 parent 007b7af commit eae1860

File tree

5 files changed

+105
-84
lines changed

5 files changed

+105
-84
lines changed

mypyc/irbuild/ll_builder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,10 @@ def call_c(self,
697697
arg = args[i]
698698
arg = self.coerce(arg, formal_type, line)
699699
coerced.append(arg)
700+
# reorder args if necessary
701+
if desc.ordering is not None:
702+
assert desc.var_arg_type is None
703+
coerced = [coerced[i] for i in desc.ordering]
700704
# coerce any var_arg
701705
var_arg_idx = -1
702706
if desc.var_arg_type is not None:

mypyc/primitives/dict_ops.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
from typing import List
44

5-
from mypyc.ir.ops import EmitterInterface, ERR_FALSE, ERR_MAGIC, ERR_NEVER
5+
from mypyc.ir.ops import EmitterInterface, ERR_FALSE, ERR_MAGIC, ERR_NEVER, ERR_NEG_INT
66
from mypyc.ir.rtypes import (
77
dict_rprimitive, object_rprimitive, bool_rprimitive, int_rprimitive,
8-
list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_pyssize_t_rprimitive
8+
list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_pyssize_t_rprimitive,
9+
c_int_rprimitive
910
)
1011

1112
from mypyc.primitives.registry import (
12-
name_ref_op, method_op, binary_op, func_op, custom_op,
13-
simple_emit, negative_int_emit, call_emit, call_negative_bool_emit,
14-
name_emit, c_custom_op, c_method_op, c_function_op
13+
name_ref_op, method_op, func_op, custom_op,
14+
simple_emit, call_emit, call_negative_bool_emit,
15+
name_emit, c_custom_op, c_method_op, c_function_op, c_binary_op
1516
)
1617

1718

@@ -39,12 +40,14 @@
3940
emit=call_negative_bool_emit('CPyDict_SetItem'))
4041

4142
# key in dict
42-
binary_op(op='in',
43-
arg_types=[object_rprimitive, dict_rprimitive],
44-
result_type=bool_rprimitive,
45-
error_kind=ERR_MAGIC,
46-
format_str='{dest} = {args[0]} in {args[1]} :: dict',
47-
emit=negative_int_emit('{dest} = PyDict_Contains({args[1]}, {args[0]});'))
43+
c_binary_op(
44+
name='in',
45+
arg_types=[object_rprimitive, dict_rprimitive],
46+
return_type=c_int_rprimitive,
47+
c_function_name='PyDict_Contains',
48+
error_kind=ERR_NEG_INT,
49+
truncated_type=bool_rprimitive,
50+
ordering=[1, 0])
4851

4952
# dict1.update(dict2)
5053
dict_update_op = method_op(

mypyc/primitives/registry.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
('c_function_name', str),
5252
('error_kind', int),
5353
('steals', StealsDescription),
54+
('ordering', Optional[List[int]]),
5455
('priority', int)])
5556

5657
# A description for C load operations including LoadGlobal and LoadAddress
@@ -352,6 +353,7 @@ def c_method_op(name: str,
352353
error_kind: int,
353354
var_arg_type: Optional[RType] = None,
354355
truncated_type: Optional[RType] = None,
356+
ordering: Optional[List[int]] = None,
355357
steals: StealsDescription = False,
356358
priority: int = 1) -> CFunctionDescription:
357359
"""Define a c function call op that replaces a method call.
@@ -368,12 +370,17 @@ def c_method_op(name: str,
368370
truncated_type: type to truncated to(See Truncate for info)
369371
if it's defined both return_type and it should be non-referenced
370372
integer types or bool type
373+
ordering: optional ordering of the arguments, if defined,
374+
reorders the arguments accordingly.
375+
should never be used together with var_arg_type.
376+
all the other arguments(such as arg_types) are in the order
377+
accepted by the python syntax(before reordering)
371378
steals: description of arguments that this steals (ref count wise)
372379
priority: if multiple ops match, the one with the highest priority is picked
373380
"""
374381
ops = c_method_call_ops.setdefault(name, [])
375382
desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type,
376-
c_function_name, error_kind, steals, priority)
383+
c_function_name, error_kind, steals, ordering, priority)
377384
ops.append(desc)
378385
return desc
379386

@@ -385,6 +392,7 @@ def c_function_op(name: str,
385392
error_kind: int,
386393
var_arg_type: Optional[RType] = None,
387394
truncated_type: Optional[RType] = None,
395+
ordering: Optional[List[int]] = None,
388396
steals: StealsDescription = False,
389397
priority: int = 1) -> CFunctionDescription:
390398
"""Define a c function call op that replaces a function call.
@@ -399,7 +407,7 @@ def c_function_op(name: str,
399407
"""
400408
ops = c_function_ops.setdefault(name, [])
401409
desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type,
402-
c_function_name, error_kind, steals, priority)
410+
c_function_name, error_kind, steals, ordering, priority)
403411
ops.append(desc)
404412
return desc
405413

@@ -411,6 +419,7 @@ def c_binary_op(name: str,
411419
error_kind: int,
412420
var_arg_type: Optional[RType] = None,
413421
truncated_type: Optional[RType] = None,
422+
ordering: Optional[List[int]] = None,
414423
steals: StealsDescription = False,
415424
priority: int = 1) -> CFunctionDescription:
416425
"""Define a c function call op for a binary operation.
@@ -422,7 +431,7 @@ def c_binary_op(name: str,
422431
"""
423432
ops = c_binary_ops.setdefault(name, [])
424433
desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type,
425-
c_function_name, error_kind, steals, priority)
434+
c_function_name, error_kind, steals, ordering, priority)
426435
ops.append(desc)
427436
return desc
428437

@@ -433,13 +442,14 @@ def c_custom_op(arg_types: List[RType],
433442
error_kind: int,
434443
var_arg_type: Optional[RType] = None,
435444
truncated_type: Optional[RType] = None,
445+
ordering: Optional[List[int]] = None,
436446
steals: StealsDescription = False) -> CFunctionDescription:
437447
"""Create a one-off CallC op that can't be automatically generated from the AST.
438448
439449
Most arguments are similar to c_method_op().
440450
"""
441451
return CFunctionDescription('<custom>', arg_types, return_type, var_arg_type, truncated_type,
442-
c_function_name, error_kind, steals, 0)
452+
c_function_name, error_kind, steals, ordering, 0)
443453

444454

445455
def c_unary_op(name: str,
@@ -448,6 +458,7 @@ def c_unary_op(name: str,
448458
c_function_name: str,
449459
error_kind: int,
450460
truncated_type: Optional[RType] = None,
461+
ordering: Optional[List[int]] = None,
451462
steals: StealsDescription = False,
452463
priority: int = 1) -> CFunctionDescription:
453464
"""Define a c function call op for an unary operation.
@@ -459,7 +470,7 @@ def c_unary_op(name: str,
459470
"""
460471
ops = c_unary_ops.setdefault(name, [])
461472
desc = CFunctionDescription(name, [arg_type], return_type, None, truncated_type,
462-
c_function_name, error_kind, steals, priority)
473+
c_function_name, error_kind, steals, ordering, priority)
463474
ops.append(desc)
464475
return desc
465476

mypyc/test-data/irbuild-dict.test

Lines changed: 65 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -86,18 +86,20 @@ def f(d):
8686
d :: dict
8787
r0 :: short_int
8888
r1 :: object
89-
r2, r3, r4 :: bool
89+
r2 :: int32
90+
r3, r4, r5 :: bool
9091
L0:
9192
r0 = 4
9293
r1 = box(short_int, r0)
93-
r2 = r1 in d :: dict
94-
if r2 goto L1 else goto L2 :: bool
94+
r2 = PyDict_Contains(d, r1)
95+
r3 = truncate r2: int32 to builtins.bool
96+
if r3 goto L1 else goto L2 :: bool
9597
L1:
96-
r3 = True
97-
return r3
98-
L2:
99-
r4 = False
98+
r4 = True
10099
return r4
100+
L2:
101+
r5 = False
102+
return r5
101103
L3:
102104
unreachable
103105

@@ -113,19 +115,21 @@ def f(d):
113115
d :: dict
114116
r0 :: short_int
115117
r1 :: object
116-
r2, r3, r4, r5 :: bool
118+
r2 :: int32
119+
r3, r4, r5, r6 :: bool
117120
L0:
118121
r0 = 4
119122
r1 = box(short_int, r0)
120-
r2 = r1 in d :: dict
121-
r3 = !r2
122-
if r3 goto L1 else goto L2 :: bool
123+
r2 = PyDict_Contains(d, r1)
124+
r3 = truncate r2: int32 to builtins.bool
125+
r4 = !r3
126+
if r4 goto L1 else goto L2 :: bool
123127
L1:
124-
r4 = True
125-
return r4
126-
L2:
127-
r5 = False
128+
r5 = True
128129
return r5
130+
L2:
131+
r6 = False
132+
return r6
129133
L3:
130134
unreachable
131135

@@ -242,20 +246,21 @@ def print_dict_methods(d1, d2):
242246
r7 :: object
243247
v, r8 :: int
244248
r9 :: object
245-
r10 :: bool
246-
r11 :: None
247-
r12, r13 :: bool
248-
r14, r15 :: short_int
249-
r16 :: int
250-
r17 :: object
251-
r18 :: tuple[bool, int, object, object]
252-
r19 :: int
253-
r20 :: bool
254-
r21, r22 :: object
255-
r23, r24, k :: int
256-
r25, r26, r27, r28, r29 :: object
257-
r30, r31, r32 :: bool
258-
r33 :: None
249+
r10 :: int32
250+
r11 :: bool
251+
r12 :: None
252+
r13, r14 :: bool
253+
r15, r16 :: short_int
254+
r17 :: int
255+
r18 :: object
256+
r19 :: tuple[bool, int, object, object]
257+
r20 :: int
258+
r21 :: bool
259+
r22, r23 :: object
260+
r24, r25, k :: int
261+
r26, r27, r28, r29, r30 :: object
262+
r31, r32, r33 :: bool
263+
r34 :: None
259264
L0:
260265
r0 = 0
261266
r1 = r0
@@ -272,47 +277,48 @@ L2:
272277
r8 = unbox(int, r7)
273278
v = r8
274279
r9 = box(int, v)
275-
r10 = r9 in d2 :: dict
276-
if r10 goto L3 else goto L4 :: bool
280+
r10 = PyDict_Contains(d2, r9)
281+
r11 = truncate r10: int32 to builtins.bool
282+
if r11 goto L3 else goto L4 :: bool
277283
L3:
278-
r11 = None
279-
return r11
284+
r12 = None
285+
return r12
280286
L4:
281287
L5:
282-
r12 = assert size(d1) == r2
288+
r13 = assert size(d1) == r2
283289
goto L1
284290
L6:
285-
r13 = no_err_occurred
291+
r14 = no_err_occurred
286292
L7:
287-
r14 = 0
288-
r15 = r14
289-
r16 = len d2 :: dict
290-
r17 = item_iter d2 :: dict
293+
r15 = 0
294+
r16 = r15
295+
r17 = len d2 :: dict
296+
r18 = item_iter d2 :: dict
291297
L8:
292-
r18 = next_item r17, offset=r15
293-
r19 = r18[1]
294-
r15 = r19
295-
r20 = r18[0]
296-
if r20 goto L9 else goto L11 :: bool
298+
r19 = next_item r18, offset=r16
299+
r20 = r19[1]
300+
r16 = r20
301+
r21 = r19[0]
302+
if r21 goto L9 else goto L11 :: bool
297303
L9:
298-
r21 = r18[2]
299-
r22 = r18[3]
300-
r23 = unbox(int, r21)
304+
r22 = r19[2]
305+
r23 = r19[3]
301306
r24 = unbox(int, r22)
302-
k = r23
303-
v = r24
304-
r25 = box(int, k)
305-
r26 = d2[r25] :: dict
306-
r27 = box(int, v)
307-
r28 = r26 += r27
308-
r29 = box(int, k)
309-
r30 = d2.__setitem__(r29, r28) :: dict
307+
r25 = unbox(int, r23)
308+
k = r24
309+
v = r25
310+
r26 = box(int, k)
311+
r27 = d2[r26] :: dict
312+
r28 = box(int, v)
313+
r29 = r27 += r28
314+
r30 = box(int, k)
315+
r31 = d2.__setitem__(r30, r29) :: dict
310316
L10:
311-
r31 = assert size(d2) == r16
317+
r32 = assert size(d2) == r17
312318
goto L8
313319
L11:
314-
r32 = no_err_occurred
320+
r33 = no_err_occurred
315321
L12:
316-
r33 = None
317-
return r33
322+
r34 = None
323+
return r34
318324

mypyc/test/test_emitfunc.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,7 @@ def test_new_dict(self) -> None:
235235
def test_dict_contains(self) -> None:
236236
self.assert_emit_binary_op(
237237
'in', self.b, self.o, self.d,
238-
"""int __tmp1 = PyDict_Contains(cpy_r_d, cpy_r_o);
239-
if (__tmp1 < 0)
240-
cpy_r_r0 = 2;
241-
else
242-
cpy_r_r0 = __tmp1;
243-
""")
238+
"""cpy_r_r0 = PyDict_Contains(cpy_r_d, cpy_r_o);""")
244239

245240
def assert_emit(self, op: Op, expected: str) -> None:
246241
self.emitter.fragments = []
@@ -270,9 +265,11 @@ def assert_emit_binary_op(self,
270265
for c_desc in c_ops:
271266
if (is_subtype(left.type, c_desc.arg_types[0])
272267
and is_subtype(right.type, c_desc.arg_types[1])):
273-
self.assert_emit(CallC(c_desc.c_function_name, [left, right],
274-
c_desc.return_type, c_desc.steals, c_desc.error_kind,
275-
55), expected)
268+
args = [left, right]
269+
if c_desc.ordering is not None:
270+
args = [args[i] for i in c_desc.ordering]
271+
self.assert_emit(CallC(c_desc.c_function_name, args, c_desc.return_type,
272+
c_desc.steals, c_desc.error_kind, 55), expected)
276273
return
277274
ops = binary_ops[op]
278275
for desc in ops:

0 commit comments

Comments
 (0)