Skip to content

Commit e7c095a

Browse files
authored
[mypyc] Refactor: use new-style primitives for function ops (#18211)
Eventually we'll get rid of the old-style CallC primitives.
1 parent 1711de8 commit e7c095a

11 files changed

+65
-57
lines changed

mypyc/irbuild/builder.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
IntOp,
8585
LoadStatic,
8686
Op,
87+
PrimitiveDescription,
8788
RaiseStandardError,
8889
Register,
8990
SetAttr,
@@ -381,6 +382,9 @@ def load_module(self, name: str) -> Value:
381382
def call_c(self, desc: CFunctionDescription, args: list[Value], line: int) -> Value:
382383
return self.builder.call_c(desc, args, line)
383384

385+
def primitive_op(self, desc: PrimitiveDescription, args: list[Value], line: int) -> Value:
386+
return self.builder.primitive_op(desc, args, line)
387+
384388
def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value:
385389
return self.builder.int_op(type, lhs, rhs, op, line)
386390

@@ -691,7 +695,7 @@ def assign(self, target: Register | AssignmentTarget, rvalue_reg: Value, line: i
691695
else:
692696
key = self.load_str(target.attr)
693697
boxed_reg = self.builder.box(rvalue_reg)
694-
self.call_c(py_setattr_op, [target.obj, key, boxed_reg], line)
698+
self.primitive_op(py_setattr_op, [target.obj, key, boxed_reg], line)
695699
elif isinstance(target, AssignmentTargetIndex):
696700
target_reg2 = self.gen_method_call(
697701
target.base, "__setitem__", [target.index, rvalue_reg], None, line
@@ -768,7 +772,7 @@ def process_iterator_tuple_assignment_helper(
768772
def process_iterator_tuple_assignment(
769773
self, target: AssignmentTargetTuple, rvalue_reg: Value, line: int
770774
) -> None:
771-
iterator = self.call_c(iter_op, [rvalue_reg], line)
775+
iterator = self.primitive_op(iter_op, [rvalue_reg], line)
772776

773777
# This may be the whole lvalue list if there is no starred value
774778
split_idx = target.star_idx if target.star_idx is not None else len(target.items)
@@ -794,7 +798,7 @@ def process_iterator_tuple_assignment(
794798
# Assign the starred value and all values after it
795799
if target.star_idx is not None:
796800
post_star_vals = target.items[split_idx + 1 :]
797-
iter_list = self.call_c(to_list, [iterator], line)
801+
iter_list = self.primitive_op(to_list, [iterator], line)
798802
iter_list_len = self.builtin_len(iter_list, line)
799803
post_star_len = Integer(len(post_star_vals))
800804
condition = self.binary_op(post_star_len, iter_list_len, "<=", line)
@@ -1051,9 +1055,9 @@ def call_refexpr_with_args(
10511055
# Handle data-driven special-cased primitive call ops.
10521056
if callee.fullname and expr.arg_kinds == [ARG_POS] * len(arg_values):
10531057
fullname = get_call_target_fullname(callee)
1054-
call_c_ops_candidates = function_ops.get(fullname, [])
1055-
target = self.builder.matching_call_c(
1056-
call_c_ops_candidates, arg_values, expr.line, self.node_type(expr)
1058+
primitive_candidates = function_ops.get(fullname, [])
1059+
target = self.builder.matching_primitive_op(
1060+
primitive_candidates, arg_values, expr.line, self.node_type(expr)
10571061
)
10581062
if target:
10591063
return target

mypyc/irbuild/classdef.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def add_attr(self, lvalue: NameExpr, stmt: AssignmentStmt) -> None:
292292
return
293293
typ = self.builder.load_native_type_object(self.cdef.fullname)
294294
value = self.builder.accept(stmt.rvalue)
295-
self.builder.call_c(
295+
self.builder.primitive_op(
296296
py_setattr_op, [typ, self.builder.load_str(lvalue.name), value], stmt.line
297297
)
298298
if self.builder.non_function_scope() and stmt.is_final_def:
@@ -452,7 +452,7 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value:
452452
)
453453
)
454454
# Populate a '__mypyc_attrs__' field containing the list of attrs
455-
builder.call_c(
455+
builder.primitive_op(
456456
py_setattr_op,
457457
[
458458
tp,
@@ -483,7 +483,7 @@ def make_generic_base_class(
483483
for tv, type_param in zip(tvs, type_args):
484484
if type_param.kind == TYPE_VAR_TUPLE_KIND:
485485
# Evaluate *Ts for a TypeVarTuple
486-
it = builder.call_c(iter_op, [tv], line)
486+
it = builder.primitive_op(iter_op, [tv], line)
487487
tv = builder.call_c(next_op, [it], line)
488488
args.append(tv)
489489

@@ -603,7 +603,7 @@ def setup_non_ext_dict(
603603
This class dictionary is passed to the metaclass constructor.
604604
"""
605605
# Check if the metaclass defines a __prepare__ method, and if so, call it.
606-
has_prepare = builder.call_c(
606+
has_prepare = builder.primitive_op(
607607
py_hasattr_op, [metaclass, builder.load_str("__prepare__")], cdef.line
608608
)
609609

mypyc/irbuild/expression.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: Supe
476476
# Grab first argument
477477
vself: Value = builder.self()
478478
if decl.kind == FUNC_CLASSMETHOD:
479-
vself = builder.call_c(type_op, [vself], expr.line)
479+
vself = builder.primitive_op(type_op, [vself], expr.line)
480480
elif builder.fn_info.is_generator:
481481
# For generator classes, the self target is the 6th value
482482
# in the symbol table (which is an ordered dict). This is sort
@@ -953,7 +953,7 @@ def transform_tuple_expr(builder: IRBuilder, expr: TupleExpr) -> Value:
953953
def _visit_tuple_display(builder: IRBuilder, expr: TupleExpr) -> Value:
954954
"""Create a list, then turn it into a tuple."""
955955
val_as_list = _visit_list_display(builder, expr.items, expr.line)
956-
return builder.call_c(list_tuple_op, [val_as_list], expr.line)
956+
return builder.primitive_op(list_tuple_op, [val_as_list], expr.line)
957957

958958

959959
def transform_dict_expr(builder: IRBuilder, expr: DictExpr) -> Value:
@@ -1045,12 +1045,12 @@ def get_arg(arg: Expression | None) -> Value:
10451045
return builder.accept(arg)
10461046

10471047
args = [get_arg(expr.begin_index), get_arg(expr.end_index), get_arg(expr.stride)]
1048-
return builder.call_c(new_slice_op, args, expr.line)
1048+
return builder.primitive_op(new_slice_op, args, expr.line)
10491049

10501050

10511051
def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value:
10521052
builder.warning("Treating generator comprehension as list", o.line)
1053-
return builder.call_c(iter_op, [translate_list_comprehension(builder, o)], o.line)
1053+
return builder.primitive_op(iter_op, [translate_list_comprehension(builder, o)], o.line)
10541054

10551055

10561056
def transform_assignment_expr(builder: IRBuilder, o: AssignmentExpr) -> Value:

mypyc/irbuild/for_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ def init(self, expr_reg: Value, target_type: RType) -> None:
586586
# for the for-loop. If we are inside of a generator function, spill these into the
587587
# environment class.
588588
builder = self.builder
589-
iter_reg = builder.call_c(iter_op, [expr_reg], self.line)
589+
iter_reg = builder.primitive_op(iter_op, [expr_reg], self.line)
590590
builder.maybe_spill(expr_reg)
591591
self.iter_target = builder.maybe_spill(iter_reg)
592592
self.target_type = target_type

mypyc/irbuild/format_str_tokenizer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,12 @@ def convert_format_expr_to_str(
146146
if is_str_rprimitive(node_type):
147147
var_str = builder.accept(x)
148148
elif is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type):
149-
var_str = builder.call_c(int_to_str_op, [builder.accept(x)], line)
149+
var_str = builder.primitive_op(int_to_str_op, [builder.accept(x)], line)
150150
else:
151-
var_str = builder.call_c(str_op, [builder.accept(x)], line)
151+
var_str = builder.primitive_op(str_op, [builder.accept(x)], line)
152152
elif format_op == FormatOp.INT:
153153
if is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type):
154-
var_str = builder.call_c(int_to_str_op, [builder.accept(x)], line)
154+
var_str = builder.primitive_op(int_to_str_op, [builder.accept(x)], line)
155155
else:
156156
return None
157157
else:

mypyc/irbuild/function.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,9 @@ def handle_ext_method(builder: IRBuilder, cdef: ClassDef, fdef: FuncDef) -> None
433433

434434
# Set the callable object representing the decorated method as an attribute of the
435435
# extension class.
436-
builder.call_c(py_setattr_op, [typ, builder.load_str(name), decorated_func], fdef.line)
436+
builder.primitive_op(
437+
py_setattr_op, [typ, builder.load_str(name), decorated_func], fdef.line
438+
)
437439

438440
if fdef.is_property:
439441
# If there is a property setter, it will be processed after the getter,
@@ -973,7 +975,7 @@ def generate_singledispatch_callable_class_ctor(builder: IRBuilder) -> None:
973975
cache_dict = builder.call_c(dict_new_op, [], line)
974976
dispatch_cache_str = builder.load_str("dispatch_cache")
975977
# use the py_setattr_op instead of SetAttr so that it also gets added to our __dict__
976-
builder.call_c(py_setattr_op, [builder.self(), dispatch_cache_str, cache_dict], line)
978+
builder.primitive_op(py_setattr_op, [builder.self(), dispatch_cache_str, cache_dict], line)
977979
# the generated C code seems to expect that __init__ returns a char, so just return 1
978980
builder.add(Return(Integer(1, bool_rprimitive, line), line))
979981

@@ -1016,7 +1018,7 @@ def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None:
10161018
registry_dict = builder.builder.make_dict([(loaded_object_type, main_func_obj)], line)
10171019

10181020
dispatch_func_obj = builder.load_global_str(fitem.name, line)
1019-
builder.call_c(
1021+
builder.primitive_op(
10201022
py_setattr_op, [dispatch_func_obj, builder.load_str("registry"), registry_dict], line
10211023
)
10221024

mypyc/irbuild/ll_builder.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ def py_get_attr(self, obj: Value, attr: str, line: int) -> Value:
620620
Prefer get_attr() which generates optimized code for native classes.
621621
"""
622622
key = self.load_str(attr)
623-
return self.call_c(py_getattr_op, [obj, key], line)
623+
return self.primitive_op(py_getattr_op, [obj, key], line)
624624

625625
# isinstance() checks
626626

@@ -656,7 +656,9 @@ def isinstance_native(self, obj: Value, class_ir: ClassIR, line: int) -> Value:
656656
"""
657657
concrete = all_concrete_classes(class_ir)
658658
if concrete is None or len(concrete) > FAST_ISINSTANCE_MAX_SUBCLASSES + 1:
659-
return self.call_c(fast_isinstance_op, [obj, self.get_native_type(class_ir)], line)
659+
return self.primitive_op(
660+
fast_isinstance_op, [obj, self.get_native_type(class_ir)], line
661+
)
660662
if not concrete:
661663
# There can't be any concrete instance that matches this.
662664
return self.false()
@@ -857,7 +859,7 @@ def _construct_varargs(
857859
if star_result is None:
858860
star_result = self.new_tuple(star_values, line)
859861
else:
860-
star_result = self.call_c(list_tuple_op, [star_result], line)
862+
star_result = self.primitive_op(list_tuple_op, [star_result], line)
861863
if has_star2 and star2_result is None:
862864
star2_result = self._create_dict(star2_keys, star2_values, line)
863865

@@ -1515,7 +1517,7 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val
15151517
# Cast to bool if necessary since most types uses comparison returning a object type
15161518
# See generic_ops.py for more information
15171519
if not is_bool_rprimitive(compare.type):
1518-
compare = self.call_c(bool_op, [compare], line)
1520+
compare = self.primitive_op(bool_op, [compare], line)
15191521
if i < len(lhs.type.types) - 1:
15201522
branch = Branch(compare, early_stop, check_blocks[i + 1], Branch.BOOL)
15211523
else:
@@ -1534,7 +1536,7 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val
15341536
def translate_instance_contains(self, inst: Value, item: Value, op: str, line: int) -> Value:
15351537
res = self.gen_method_call(inst, "__contains__", [item], None, line)
15361538
if not is_bool_rprimitive(res.type):
1537-
res = self.call_c(bool_op, [res], line)
1539+
res = self.primitive_op(bool_op, [res], line)
15381540
if op == "not in":
15391541
res = self.bool_bitwise_op(res, Integer(1, rtype=bool_rprimitive), "^", line)
15401542
return res
@@ -1667,7 +1669,7 @@ def new_list_op(self, values: list[Value], line: int) -> Value:
16671669
return result_list
16681670

16691671
def new_set_op(self, values: list[Value], line: int) -> Value:
1670-
return self.call_c(new_set_op, values, line)
1672+
return self.primitive_op(new_set_op, values, line)
16711673

16721674
def setup_rarray(
16731675
self, item_type: RType, values: Sequence[Value], *, object_ptr: bool = False
@@ -1775,7 +1777,7 @@ def bool_value(self, value: Value) -> Value:
17751777
self.goto(end)
17761778
self.activate_block(end)
17771779
else:
1778-
result = self.call_c(bool_op, [value], value.line)
1780+
result = self.primitive_op(bool_op, [value], value.line)
17791781
return result
17801782

17811783
def add_bool_branch(self, value: Value, true: BasicBlock, false: BasicBlock) -> None:
@@ -2065,7 +2067,7 @@ def float_mod(self, lhs: Value, rhs: Value, line: int) -> Value:
20652067
self.activate_block(copysign)
20662068
# If the remainder is zero, CPython ensures the result has the
20672069
# same sign as the denominator.
2068-
adj = self.call_c(copysign_op, [Float(0.0), rhs], line)
2070+
adj = self.primitive_op(copysign_op, [Float(0.0), rhs], line)
20692071
self.add(Assign(res, adj))
20702072
self.add(Goto(done))
20712073
self.activate_block(done)
@@ -2260,7 +2262,7 @@ def new_tuple_with_length(self, length: Value, line: int) -> Value:
22602262
return self.call_c(new_tuple_with_length_op, [length], line)
22612263

22622264
def int_to_float(self, n: Value, line: int) -> Value:
2263-
return self.call_c(int_to_float_op, [n], line)
2265+
return self.primitive_op(int_to_float_op, [n], line)
22642266

22652267
# Internal helpers
22662268

mypyc/irbuild/match.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def visit_class_pattern(self, pattern: ClassPattern) -> None:
131131
else slow_isinstance_op
132132
)
133133

134-
cond = self.builder.call_c(
134+
cond = self.builder.primitive_op(
135135
isinstance_op, [self.subject, self.builder.accept(pattern.class_ref)], pattern.line
136136
)
137137

@@ -246,7 +246,7 @@ def visit_mapping_pattern(self, pattern: MappingPattern) -> None:
246246
self.builder.activate_block(self.code_block)
247247
self.code_block = BasicBlock()
248248

249-
rest = self.builder.call_c(dict_copy, [self.subject], pattern.rest.line)
249+
rest = self.builder.primitive_op(dict_copy, [self.subject], pattern.rest.line)
250250

251251
target = self.builder.get_assignment_target(pattern.rest)
252252

mypyc/irbuild/statement.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
LoadLiteral,
5959
LoadStatic,
6060
MethodCall,
61+
PrimitiveDescription,
6162
RaiseStandardError,
6263
Register,
6364
Return,
@@ -757,7 +758,7 @@ def transform_with(
757758
value = builder.add(MethodCall(mgr_v, f"__{al}enter__", args=[], line=line))
758759
exit_ = None
759760
else:
760-
typ = builder.call_c(type_op, [mgr_v], line)
761+
typ = builder.primitive_op(type_op, [mgr_v], line)
761762
exit_ = builder.maybe_spill(builder.py_get_attr(typ, f"__{al}exit__", line))
762763
value = builder.py_call(builder.py_get_attr(typ, f"__{al}enter__", line), [mgr_v], line)
763764

@@ -876,7 +877,7 @@ def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int)
876877
line,
877878
)
878879
key = builder.load_str(target.attr)
879-
builder.call_c(py_delattr_op, [target.obj, key], line)
880+
builder.primitive_op(py_delattr_op, [target.obj, key], line)
880881
elif isinstance(target, AssignmentTargetRegister):
881882
# Delete a local by assigning an error value to it, which will
882883
# prompt the insertion of uninit checks.
@@ -924,7 +925,10 @@ def emit_yield_from_or_await(
924925
received_reg = Register(object_rprimitive)
925926

926927
get_op = coro_op if is_await else iter_op
927-
iter_val = builder.call_c(get_op, [val], line)
928+
if isinstance(get_op, PrimitiveDescription):
929+
iter_val = builder.primitive_op(get_op, [val], line)
930+
else:
931+
iter_val = builder.call_c(get_op, [val], line)
928932

929933
iter_reg = builder.maybe_spill_assignable(iter_val)
930934

mypyc/primitives/registry.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ class LoadAddressDescription(NamedTuple):
7070
src: str # name of the target to load
7171

7272

73-
# CallC op for method call(such as 'str.join')
73+
# CallC op for method call (such as 'str.join')
7474
method_call_ops: dict[str, list[CFunctionDescription]] = {}
7575

76-
# CallC op for top level function call(such as 'builtins.list')
77-
function_ops: dict[str, list[CFunctionDescription]] = {}
76+
# Primitive ops for top level function call (such as 'builtins.list')
77+
function_ops: dict[str, list[PrimitiveDescription]] = {}
7878

79-
# CallC op for binary ops
79+
# Primitive ops for binary operations
8080
binary_ops: dict[str, list[PrimitiveDescription]] = {}
8181

8282
# CallC op for unary ops
@@ -161,8 +161,8 @@ def function_op(
161161
steals: StealsDescription = False,
162162
is_borrowed: bool = False,
163163
priority: int = 1,
164-
) -> CFunctionDescription:
165-
"""Define a c function call op that replaces a function call.
164+
) -> PrimitiveDescription:
165+
"""Define a C function call op that replaces a function call.
166166
167167
This will be automatically generated by matching against the AST.
168168
@@ -175,19 +175,19 @@ def function_op(
175175
if extra_int_constants is None:
176176
extra_int_constants = []
177177
ops = function_ops.setdefault(name, [])
178-
desc = CFunctionDescription(
178+
desc = PrimitiveDescription(
179179
name,
180180
arg_types,
181181
return_type,
182-
var_arg_type,
183-
truncated_type,
184-
c_function_name,
185-
error_kind,
186-
steals,
187-
is_borrowed,
188-
ordering,
189-
extra_int_constants,
190-
priority,
182+
var_arg_type=var_arg_type,
183+
truncated_type=truncated_type,
184+
c_function_name=c_function_name,
185+
error_kind=error_kind,
186+
steals=steals,
187+
is_borrowed=is_borrowed,
188+
ordering=ordering,
189+
extra_int_constants=extra_int_constants,
190+
priority=priority,
191191
is_pure=False,
192192
)
193193
ops.append(desc)

mypyc/test/test_cheader.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,14 @@ def check_name(name: str) -> None:
2626
rf"\b{name}\b", header
2727
), f'"{name}" is used in mypyc.primitives but not declared in CPy.h'
2828

29-
for old_values in [
30-
registry.method_call_ops.values(),
31-
registry.function_ops.values(),
32-
registry.unary_ops.values(),
33-
]:
29+
for old_values in [registry.method_call_ops.values(), registry.unary_ops.values()]:
3430
for old_ops in old_values:
3531
if isinstance(old_ops, CFunctionDescription):
3632
old_ops = [old_ops]
3733
for old_op in old_ops:
3834
check_name(old_op.c_function_name)
3935

40-
for values in [registry.binary_ops.values()]:
36+
for values in [registry.binary_ops.values(), registry.function_ops.values()]:
4137
for ops in values:
4238
if isinstance(ops, PrimitiveDescription):
4339
ops = [ops]

0 commit comments

Comments
 (0)