From c43647280a0d6941cdf350cce02ba8bb87677424 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 29 Nov 2024 16:41:42 +0000 Subject: [PATCH 1/4] [mypyc] Refactor: use new-style primitives for unary ops --- mypyc/ir/ops.py | 2 +- mypyc/irbuild/ll_builder.py | 4 ++-- mypyc/primitives/int_ops.py | 11 ++--------- mypyc/primitives/registry.py | 28 ++++++++++++++-------------- mypyc/test-data/irbuild-int.test | 20 +++++++++++++++++++- mypyc/test/test_cheader.py | 8 ++++++-- mypyc/test/test_emitfunc.py | 1 + 7 files changed, 45 insertions(+), 29 deletions(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 896ba3ac091c..6e186c4ef0fc 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -625,7 +625,7 @@ def __init__( assert error_kind == ERR_NEVER def __repr__(self) -> str: - return f"" + return f"" class PrimitiveOp(RegisterOp): diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index df73f8481751..a5a524b25af0 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1601,8 +1601,8 @@ def unary_op(self, value: Value, expr_op: str, line: int) -> Value: result = self.dunder_op(value, None, expr_op, line) if result is not None: return result - call_c_ops_candidates = unary_ops.get(expr_op, []) - target = self.matching_call_c(call_c_ops_candidates, [value], line) + primitive_ops_candidates = unary_ops.get(expr_op, []) + target = self.matching_primitive_op(primitive_ops_candidates, [value], line) assert target, "Unsupported unary operation: %s" % expr_op return target diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 2eff233403f4..657578d20046 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -31,14 +31,7 @@ str_rprimitive, void_rtype, ) -from mypyc.primitives.registry import ( - CFunctionDescription, - binary_op, - custom_op, - function_op, - load_address_op, - unary_op, -) +from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, unary_op # Constructors for builtins.int and native int types have the same behavior. In # interpreted mode, native int types are just aliases to 'int'. @@ -176,7 +169,7 @@ def int_binary_op( int_binary_op("<<=", "CPyTagged_Lshift", error_kind=ERR_MAGIC) -def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: +def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: return unary_op( name=name, arg_type=int_rprimitive, diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 25ad83be14f5..26faabc0c1e2 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -79,8 +79,8 @@ class LoadAddressDescription(NamedTuple): # Primitive ops for binary operations binary_ops: dict[str, list[PrimitiveDescription]] = {} -# CallC op for unary ops -unary_ops: dict[str, list[CFunctionDescription]] = {} +# Primitive ops for unary ops +unary_ops: dict[str, list[PrimitiveDescription]] = {} builtin_names: dict[str, tuple[RType, str]] = {} @@ -327,8 +327,8 @@ def unary_op( is_borrowed: bool = False, priority: int = 1, is_pure: bool = False, -) -> CFunctionDescription: - """Define a c function call op for an unary operation. +) -> PrimitiveDescription: + """Define a primitive op for an unary operation. This will be automatically generated by matching against the AST. @@ -338,19 +338,19 @@ def unary_op( if extra_int_constants is None: extra_int_constants = [] ops = unary_ops.setdefault(name, []) - desc = CFunctionDescription( + desc = PrimitiveDescription( name, [arg_type], return_type, - None, - truncated_type, - c_function_name, - error_kind, - steals, - is_borrowed, - ordering, - extra_int_constants, - priority, + var_arg_type=None, + truncated_type=truncated_type, + c_function_name=c_function_name, + error_kind=error_kind, + steals=steals, + is_borrowed=is_borrowed, + ordering=ordering, + extra_int_constants=extra_int_constants, + priority=priority, is_pure=is_pure, ) ops.append(desc) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index b1a712103e70..9082cc0136d9 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -182,13 +182,31 @@ def int_to_int(n): L0: return n -[case testIntUnaryPlus] +[case testIntUnaryOps] +def unary_minus(n: int) -> int: + x = -n + return x def unary_plus(n: int) -> int: x = +n return x +def unary_invert(n: int) -> int: + x = ~n + return x [out] +def unary_minus(n): + n, r0, x :: int +L0: + r0 = CPyTagged_Negate(n) + x = r0 + return x def unary_plus(n): n, x :: int L0: x = n return x +def unary_invert(n): + n, r0, x :: int +L0: + r0 = CPyTagged_Invert(n) + x = r0 + return x diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py index 08b5a4648ca5..9c09f14e93dd 100644 --- a/mypyc/test/test_cheader.py +++ b/mypyc/test/test_cheader.py @@ -26,14 +26,18 @@ def check_name(name: str) -> None: rf"\b{name}\b", header ), f'"{name}" is used in mypyc.primitives but not declared in CPy.h' - for old_values in [registry.method_call_ops.values(), registry.unary_ops.values()]: + for old_values in [registry.method_call_ops.values()]: for old_ops in old_values: if isinstance(old_ops, CFunctionDescription): old_ops = [old_ops] for old_op in old_ops: check_name(old_op.c_function_name) - for values in [registry.binary_ops.values(), registry.function_ops.values()]: + for values in [ + registry.binary_ops.values(), + registry.unary_ops.values(), + registry.function_ops.values(), + ]: for ops in values: if isinstance(ops, PrimitiveDescription): ops = [ops] diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 273ba409fac2..b97ad78d6970 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -154,6 +154,7 @@ def test_int_sub(self) -> None: ) def test_int_neg(self) -> None: + assert int_neg_op.c_function_name is not None self.assert_emit( CallC( int_neg_op.c_function_name, From 0b8f01c5dfd70891db5c44d2d3b9cb01f2dbfef1 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 29 Nov 2024 17:03:47 +0000 Subject: [PATCH 2/4] WIP use new-style primitives for method ops --- mypyc/irbuild/builder.py | 8 ++-- mypyc/irbuild/classdef.py | 8 ++-- mypyc/irbuild/expression.py | 13 ++--- mypyc/irbuild/for_helpers.py | 4 +- mypyc/irbuild/function.py | 8 ++-- mypyc/irbuild/ll_builder.py | 10 ++-- mypyc/primitives/registry.py | 8 ++-- mypyc/test/test_cheader.py | 9 +--- mypyc/test/test_emitfunc.py | 92 +----------------------------------- 9 files changed, 32 insertions(+), 128 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 7561070b012f..a0837ba2bfc7 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -404,7 +404,7 @@ def add_to_non_ext_dict( ) -> None: # Add an attribute entry into the class dict of a non-extension class. key_unicode = self.load_str(key) - self.call_c(dict_set_item_op, [non_ext.dict, key_unicode, val], line) + self.primitive_op(dict_set_item_op, [non_ext.dict, key_unicode, val], line) def gen_import(self, id: str, line: int) -> None: self.imports[id] = None @@ -435,7 +435,7 @@ def get_module(self, module: str, line: int) -> Value: # Python 3.7 has a nice 'PyImport_GetModule' function that we can't use :( mod_dict = self.call_c(get_module_dict_op, [], line) # Get module object from modules dict. - return self.call_c(dict_get_item_op, [mod_dict, self.load_str(module)], line) + return self.primitive_op(dict_get_item_op, [mod_dict, self.load_str(module)], line) def get_module_attr(self, module: str, attr: str, line: int) -> Value: """Look up an attribute of a module without storing it in the local namespace. @@ -817,7 +817,7 @@ def process_iterator_tuple_assignment( self.activate_block(ok_block) for litem in reversed(post_star_vals): - ritem = self.call_c(list_pop_last, [iter_list], line) + ritem = self.primitive_op(list_pop_last, [iter_list], line) self.assign(litem, ritem, line) # Assign the starred value @@ -1302,7 +1302,7 @@ def load_global(self, expr: NameExpr) -> Value: def load_global_str(self, name: str, line: int) -> Value: _globals = self.load_globals_dict() reg = self.load_str(name) - return self.call_c(dict_get_item_op, [_globals, reg], line) + return self.primitive_op(dict_get_item_op, [_globals, reg], line) def load_globals_dict(self) -> Value: return self.add(LoadStatic(dict_rprimitive, "globals", self.module_name)) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 58be87c88a3f..6072efa2c593 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -256,7 +256,7 @@ def finalize(self, ir: ClassIR) -> None: ) # Add the non-extension class to the dict - self.builder.call_c( + self.builder.primitive_op( dict_set_item_op, [ self.builder.load_globals_dict(), @@ -466,7 +466,7 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add it to the dict - builder.call_c( + builder.primitive_op( dict_set_item_op, [builder.load_globals_dict(), builder.load_str(cdef.name), tp], cdef.line ) @@ -493,7 +493,7 @@ def make_generic_base_class( else: arg = builder.new_tuple(args, line) - base = builder.call_c(py_get_item_op, [gent, arg], line) + base = builder.primitive_op(py_get_item_op, [gent, arg], line) return base @@ -661,7 +661,7 @@ def add_non_ext_class_attr_ann( typ = builder.add(LoadAddress(type_object_op.type, type_object_op.src, stmt.line)) key = builder.load_str(lvalue.name) - builder.call_c(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) + builder.primitive_op(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) def add_non_ext_class_attr( diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index a2a3203d12ca..44251fceceab 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -59,6 +59,7 @@ Integer, LoadAddress, LoadLiteral, + PrimitiveDescription, RaiseStandardError, Register, TupleGet, @@ -98,7 +99,7 @@ from mypyc.primitives.generic_ops import iter_op from mypyc.primitives.list_ops import list_append_op, list_extend_op, list_slice_op from mypyc.primitives.misc_ops import ellipsis_op, get_module_dict_op, new_slice_op, type_op -from mypyc.primitives.registry import CFunctionDescription, builtin_names +from mypyc.primitives.registry import builtin_names from mypyc.primitives.set_ops import set_add_op, set_in_op, set_update_op from mypyc.primitives.str_ops import str_slice_op from mypyc.primitives.tuple_ops import list_tuple_op, tuple_slice_op @@ -181,7 +182,7 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: # AST doesn't include a Var node for the module. We # instead load the module separately on each access. mod_dict = builder.call_c(get_module_dict_op, [], expr.line) - obj = builder.call_c( + obj = builder.primitive_op( dict_get_item_op, [mod_dict, builder.load_str(expr.node.fullname)], expr.line ) return obj @@ -977,8 +978,8 @@ def _visit_display( builder: IRBuilder, items: list[Expression], constructor_op: Callable[[list[Value], int], Value], - append_op: CFunctionDescription, - extend_op: CFunctionDescription, + append_op: PrimitiveDescription, + extend_op: PrimitiveDescription, line: int, is_list: bool, ) -> Value: @@ -999,7 +1000,7 @@ def _visit_display( if result is None: result = constructor_op(initial_items, line) - builder.call_c(extend_op if starred else append_op, [result, value], line) + builder.primitive_op(extend_op if starred else append_op, [result, value], line) if result is None: result = constructor_op(initial_items, line) @@ -1028,7 +1029,7 @@ def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehe def gen_inner_stmts() -> None: k = builder.accept(o.key) v = builder.accept(o.value) - builder.call_c(dict_set_item_op, [builder.read(d), k, v], o.line) + builder.primitive_op(dict_set_item_op, [builder.read(d), k, v], o.line) comprehension_helper(builder, loop_params, gen_inner_stmts, o.line) return builder.read(d) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index b37c39e70f11..9b34a094db60 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -251,7 +251,7 @@ def translate_list_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Valu def gen_inner_stmts() -> None: e = builder.accept(gen.left_expr) - builder.call_c(list_append_op, [builder.read(list_ops), e], gen.line) + builder.primitive_op(list_append_op, [builder.read(list_ops), e], gen.line) comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line) return builder.read(list_ops) @@ -286,7 +286,7 @@ def translate_set_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Value def gen_inner_stmts() -> None: e = builder.accept(gen.left_expr) - builder.call_c(set_add_op, [builder.read(set_ops), e], gen.line) + builder.primitive_op(set_add_op, [builder.read(set_ops), e], gen.line) comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line) return builder.read(set_ops) diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 4d04c549d392..a84db5a08863 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -133,7 +133,7 @@ def transform_decorator(builder: IRBuilder, dec: Decorator) -> None: if decorated_func is not None: # Set the callable object representing the decorated function as a global. - builder.call_c( + builder.primitive_op( dict_set_item_op, [builder.load_globals_dict(), builder.load_str(dec.func.name), decorated_func], decorated_func.line, @@ -849,7 +849,7 @@ def generate_singledispatch_dispatch_function( dispatch_func_obj, "dispatch_cache", dict_rprimitive, line ) call_find_impl, use_cache, call_func = BasicBlock(), BasicBlock(), BasicBlock() - get_result = builder.call_c(dict_get_method_with_none, [dispatch_cache, arg_type], line) + get_result = builder.primitive_op(dict_get_method_with_none, [dispatch_cache, arg_type], line) is_not_none = builder.translate_is_op(get_result, builder.none_object(), "is not", line) impl_to_use = Register(object_rprimitive) builder.add_bool_branch(is_not_none, use_cache, call_find_impl) @@ -862,7 +862,7 @@ def generate_singledispatch_dispatch_function( find_impl = builder.load_module_attr_by_fullname("functools._find_impl", line) registry = load_singledispatch_registry(builder, dispatch_func_obj, line) uncached_impl = builder.py_call(find_impl, [arg_type, registry], line) - builder.call_c(dict_set_item_op, [dispatch_cache, arg_type, uncached_impl], line) + builder.primitive_op(dict_set_item_op, [dispatch_cache, arg_type, uncached_impl], line) builder.assign(impl_to_use, uncached_impl, line) builder.goto(call_func) @@ -1039,7 +1039,7 @@ def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None: registry = load_singledispatch_registry(builder, dispatch_func_obj, line) for typ in types: loaded_type = load_type(builder, typ, line) - builder.call_c(dict_set_item_op, [registry, loaded_type, to_insert], line) + builder.primitive_op(dict_set_item_op, [registry, loaded_type, to_insert], line) dispatch_cache = builder.builder.get_attr( dispatch_func_obj, "dispatch_cache", dict_rprimitive, line ) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index a5a524b25af0..af7500d1df42 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -762,7 +762,7 @@ def _construct_varargs( if kind == ARG_STAR: if star_result is None: star_result = self.new_list_op(star_values, line) - self.call_c(list_extend_op, [star_result, value], line) + self.primitive_op(list_extend_op, [star_result, value], line) elif kind == ARG_STAR2: if star2_result is None: star2_result = self._create_dict(star2_keys, star2_values, line) @@ -2346,11 +2346,11 @@ def translate_special_method_call( Return None if no translation found; otherwise return the target register. """ - call_c_ops_candidates = method_call_ops.get(name, []) - call_c_op = self.matching_call_c( - call_c_ops_candidates, [base_reg] + args, line, result_type, can_borrow=can_borrow + primitive_ops_candidates = method_call_ops.get(name, []) + primitive_op = self.matching_primitive_op( + primitive_ops_candidates, [base_reg] + args, line, result_type, can_borrow=can_borrow ) - return call_c_op + return primitive_op def translate_eq_cmp(self, lreg: Value, rreg: Value, expr_op: str, line: int) -> Value | None: """Add a equality comparison operation. diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 26faabc0c1e2..5e7ecb70f55d 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -70,8 +70,8 @@ class LoadAddressDescription(NamedTuple): src: str # name of the target to load -# CallC op for method call (such as 'str.join') -method_call_ops: dict[str, list[CFunctionDescription]] = {} +# Primitive ops for method call (such as 'str.join') +method_call_ops: dict[str, list[PrimitiveDescription]] = {} # Primitive ops for top level function call (such as 'builtins.list') function_ops: dict[str, list[PrimitiveDescription]] = {} @@ -99,7 +99,7 @@ def method_op( is_borrowed: bool = False, priority: int = 1, is_pure: bool = False, -) -> CFunctionDescription: +) -> PrimitiveDescription: """Define a c function call op that replaces a method call. This will be automatically generated by matching against the AST. @@ -129,7 +129,7 @@ def method_op( if extra_int_constants is None: extra_int_constants = [] ops = method_call_ops.setdefault(name, []) - desc = CFunctionDescription( + desc = PrimitiveDescription( name, arg_types, return_type, diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py index 9c09f14e93dd..7ab055c735ad 100644 --- a/mypyc/test/test_cheader.py +++ b/mypyc/test/test_cheader.py @@ -9,7 +9,6 @@ from mypyc.ir.ops import PrimitiveDescription from mypyc.primitives import registry -from mypyc.primitives.registry import CFunctionDescription class TestHeaderInclusion(unittest.TestCase): @@ -26,14 +25,8 @@ def check_name(name: str) -> None: rf"\b{name}\b", header ), f'"{name}" is used in mypyc.primitives but not declared in CPy.h' - for old_values in [registry.method_call_ops.values()]: - for old_ops in old_values: - if isinstance(old_ops, CFunctionDescription): - old_ops = [old_ops] - for old_op in old_ops: - check_name(old_op.c_function_name) - for values in [ + registry.method_call_ops.values(), registry.binary_ops.values(), registry.unary_ops.values(), registry.function_ops.values(), diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index b97ad78d6970..bc867bb0ef07 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -59,14 +59,8 @@ ) from mypyc.irbuild.vtable import compute_vtable from mypyc.namegen import NameGenerator -from mypyc.primitives.dict_ops import ( - dict_get_item_op, - dict_new_op, - dict_set_item_op, - dict_update_op, -) +from mypyc.primitives.dict_ops import dict_new_op from mypyc.primitives.int_ops import int_neg_op -from mypyc.primitives.list_ops import list_append_op, list_get_item_op, list_set_item_op from mypyc.primitives.misc_ops import none_object_op from mypyc.primitives.registry import binary_ops from mypyc.subtype import is_subtype @@ -300,34 +294,6 @@ def test_dec_ref_tuple(self) -> None: def test_dec_ref_tuple_nested(self) -> None: self.assert_emit(DecRef(self.tt), "CPyTagged_DECREF(cpy_r_tt.f0.f0);") - def test_list_get_item(self) -> None: - self.assert_emit( - CallC( - list_get_item_op.c_function_name, - [self.m, self.k], - list_get_item_op.return_type, - list_get_item_op.steals, - list_get_item_op.is_borrowed, - list_get_item_op.error_kind, - 55, - ), - """cpy_r_r0 = CPyList_GetItem(cpy_r_m, cpy_r_k);""", - ) - - def test_list_set_item(self) -> None: - self.assert_emit( - CallC( - list_set_item_op.c_function_name, - [self.l, self.n, self.o], - list_set_item_op.return_type, - list_set_item_op.steals, - list_set_item_op.is_borrowed, - list_set_item_op.error_kind, - 55, - ), - """cpy_r_r0 = CPyList_SetItem(cpy_r_l, cpy_r_n, cpy_r_o);""", - ) - def test_box_int(self) -> None: self.assert_emit(Box(self.n), """cpy_r_r0 = CPyTagged_StealAsObject(cpy_r_n);""") @@ -350,20 +316,6 @@ def test_unbox_i64(self) -> None: Unbox(self.o, int64_rprimitive, 55), """cpy_r_r0 = CPyLong_AsInt64(cpy_r_o);""" ) - def test_list_append(self) -> None: - self.assert_emit( - CallC( - list_append_op.c_function_name, - [self.l, self.o], - list_append_op.return_type, - list_append_op.steals, - list_append_op.is_borrowed, - list_append_op.error_kind, - 1, - ), - """cpy_r_r0 = PyList_Append(cpy_r_l, cpy_r_o);""", - ) - def test_get_attr(self) -> None: self.assert_emit( GetAttr(self.r, "y", 1), @@ -490,48 +442,6 @@ def test_set_attr_init_with_bitmap(self) -> None: """, ) - def test_dict_get_item(self) -> None: - self.assert_emit( - CallC( - dict_get_item_op.c_function_name, - [self.d, self.o2], - dict_get_item_op.return_type, - dict_get_item_op.steals, - dict_get_item_op.is_borrowed, - dict_get_item_op.error_kind, - 1, - ), - """cpy_r_r0 = CPyDict_GetItem(cpy_r_d, cpy_r_o2);""", - ) - - def test_dict_set_item(self) -> None: - self.assert_emit( - CallC( - dict_set_item_op.c_function_name, - [self.d, self.o, self.o2], - dict_set_item_op.return_type, - dict_set_item_op.steals, - dict_set_item_op.is_borrowed, - dict_set_item_op.error_kind, - 1, - ), - """cpy_r_r0 = CPyDict_SetItem(cpy_r_d, cpy_r_o, cpy_r_o2);""", - ) - - def test_dict_update(self) -> None: - self.assert_emit( - CallC( - dict_update_op.c_function_name, - [self.d, self.o], - dict_update_op.return_type, - dict_update_op.steals, - dict_update_op.is_borrowed, - dict_update_op.error_kind, - 1, - ), - """cpy_r_r0 = CPyDict_Update(cpy_r_d, cpy_r_o);""", - ) - def test_new_dict(self) -> None: self.assert_emit( CallC( From df06932375700572e37ffae11c0e106d9aec7acb Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 2 Dec 2024 10:42:35 +0000 Subject: [PATCH 3/4] Fix propagation of result type --- mypyc/irbuild/ll_builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index af7500d1df42..556d753b89f8 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1939,7 +1939,7 @@ def primitive_op( desc.priority, is_pure=desc.is_pure, ) - return self.call_c(c_desc, args, line, result_type) + return self.call_c(c_desc, args, line, result_type=result_type) # This primitive gets transformed in a lowering pass to # lower-level IR ops using a custom transform function. @@ -2005,7 +2005,7 @@ def matching_primitive_op( else: matching = desc if matching: - return self.primitive_op(matching, args, line=line) + return self.primitive_op(matching, args, line=line, result_type=result_type) return None def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int = -1) -> Value: From 3a28025d63a92f729630c96e4803a6432eee1f88 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 2 Dec 2024 10:54:19 +0000 Subject: [PATCH 4/4] Update tests --- mypyc/test/test_emitfunc.py | 92 ++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index bc867bb0ef07..90df131288f9 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -59,8 +59,14 @@ ) from mypyc.irbuild.vtable import compute_vtable from mypyc.namegen import NameGenerator -from mypyc.primitives.dict_ops import dict_new_op +from mypyc.primitives.dict_ops import ( + dict_get_item_op, + dict_new_op, + dict_set_item_op, + dict_update_op, +) from mypyc.primitives.int_ops import int_neg_op +from mypyc.primitives.list_ops import list_append_op, list_get_item_op, list_set_item_op from mypyc.primitives.misc_ops import none_object_op from mypyc.primitives.registry import binary_ops from mypyc.subtype import is_subtype @@ -294,6 +300,34 @@ def test_dec_ref_tuple(self) -> None: def test_dec_ref_tuple_nested(self) -> None: self.assert_emit(DecRef(self.tt), "CPyTagged_DECREF(cpy_r_tt.f0.f0);") + def test_list_get_item(self) -> None: + self.assert_emit( + CallC( + str(list_get_item_op.c_function_name), + [self.m, self.k], + list_get_item_op.return_type, + list_get_item_op.steals, + list_get_item_op.is_borrowed, + list_get_item_op.error_kind, + 55, + ), + """cpy_r_r0 = CPyList_GetItem(cpy_r_m, cpy_r_k);""", + ) + + def test_list_set_item(self) -> None: + self.assert_emit( + CallC( + str(list_set_item_op.c_function_name), + [self.l, self.n, self.o], + list_set_item_op.return_type, + list_set_item_op.steals, + list_set_item_op.is_borrowed, + list_set_item_op.error_kind, + 55, + ), + """cpy_r_r0 = CPyList_SetItem(cpy_r_l, cpy_r_n, cpy_r_o);""", + ) + def test_box_int(self) -> None: self.assert_emit(Box(self.n), """cpy_r_r0 = CPyTagged_StealAsObject(cpy_r_n);""") @@ -316,6 +350,20 @@ def test_unbox_i64(self) -> None: Unbox(self.o, int64_rprimitive, 55), """cpy_r_r0 = CPyLong_AsInt64(cpy_r_o);""" ) + def test_list_append(self) -> None: + self.assert_emit( + CallC( + str(list_append_op.c_function_name), + [self.l, self.o], + list_append_op.return_type, + list_append_op.steals, + list_append_op.is_borrowed, + list_append_op.error_kind, + 1, + ), + """cpy_r_r0 = PyList_Append(cpy_r_l, cpy_r_o);""", + ) + def test_get_attr(self) -> None: self.assert_emit( GetAttr(self.r, "y", 1), @@ -442,6 +490,48 @@ def test_set_attr_init_with_bitmap(self) -> None: """, ) + def test_dict_get_item(self) -> None: + self.assert_emit( + CallC( + str(dict_get_item_op.c_function_name), + [self.d, self.o2], + dict_get_item_op.return_type, + dict_get_item_op.steals, + dict_get_item_op.is_borrowed, + dict_get_item_op.error_kind, + 1, + ), + """cpy_r_r0 = CPyDict_GetItem(cpy_r_d, cpy_r_o2);""", + ) + + def test_dict_set_item(self) -> None: + self.assert_emit( + CallC( + str(dict_set_item_op.c_function_name), + [self.d, self.o, self.o2], + dict_set_item_op.return_type, + dict_set_item_op.steals, + dict_set_item_op.is_borrowed, + dict_set_item_op.error_kind, + 1, + ), + """cpy_r_r0 = CPyDict_SetItem(cpy_r_d, cpy_r_o, cpy_r_o2);""", + ) + + def test_dict_update(self) -> None: + self.assert_emit( + CallC( + str(dict_update_op.c_function_name), + [self.d, self.o], + dict_update_op.return_type, + dict_update_op.steals, + dict_update_op.is_borrowed, + dict_update_op.error_kind, + 1, + ), + """cpy_r_r0 = CPyDict_Update(cpy_r_d, cpy_r_o);""", + ) + def test_new_dict(self) -> None: self.assert_emit( CallC(