Skip to content

Commit

Permalink
[mypyc] Merge more primitive ops (python#9110)
Browse files Browse the repository at this point in the history
Relates to mypyc/mypyc#734. 

This PR completes ALL ops of dict, str, list, tuple, set that are supported using 
current design. The remaining ones would rather need to split into multiple ops
(via specializers) or using pointers.
  • Loading branch information
TH3CHARLie authored Jul 9, 2020
1 parent ffdbeb3 commit b1f5121
Show file tree
Hide file tree
Showing 22 changed files with 325 additions and 332 deletions.
7 changes: 6 additions & 1 deletion mypyc/codegen/emit.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
is_float_rprimitive, is_bool_rprimitive, is_int_rprimitive, is_short_int_rprimitive,
is_list_rprimitive, is_dict_rprimitive, is_set_rprimitive, is_tuple_rprimitive,
is_none_rprimitive, is_object_rprimitive, object_rprimitive, is_str_rprimitive,
int_rprimitive, is_optional_type, optional_value_type
int_rprimitive, is_optional_type, optional_value_type, is_int32_rprimitive, is_int64_rprimitive
)
from mypyc.ir.func_ir import FuncDecl
from mypyc.ir.class_ir import ClassIR, all_concrete_classes
Expand Down Expand Up @@ -695,6 +695,11 @@ def emit_box(self, src: str, dest: str, typ: RType, declare_dest: bool = False,
self.emit_lines('{}{} = Py_None;'.format(declaration, dest))
if not can_borrow:
self.emit_inc_ref(dest, object_rprimitive)
# TODO: This is a hack to handle mypy's false negative on unreachable code.
# All ops returning int32/int64 should not be coerced into object.
# Since their result will not be used elsewhere, it's safe to use NULL here
elif is_int32_rprimitive(typ) or is_int64_rprimitive(typ):
self.emit_lines('{}{} = NULL;'.format(declaration, dest))
elif isinstance(typ, RTuple):
self.declare_tuple_struct(typ)
self.emit_line('{}{} = PyTuple_New({});'.format(declaration, dest, len(typ.types)))
Expand Down
4 changes: 2 additions & 2 deletions mypyc/irbuild/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def add_to_non_ext_dict(self, non_ext: NonExtClassInfo,
key: str, val: Value, line: int) -> None:
# Add an attribute entry into the class dict of a non-extension class.
key_unicode = self.load_static_unicode(key)
self.primitive_op(dict_set_item_op, [non_ext.dict, key_unicode, val], line)
self.call_c(dict_set_item_op, [non_ext.dict, key_unicode, val], line)

def gen_import(self, id: str, line: int) -> None:
self.imports[id] = None
Expand Down Expand Up @@ -884,7 +884,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_static_unicode(name)
return self.primitive_op(dict_get_item_op, [_globals, reg], line)
return self.call_c(dict_get_item_op, [_globals, reg], line)

def load_globals_dict(self) -> Value:
return self.add(LoadStatic(dict_rprimitive, 'globals', self.module_name))
Expand Down
32 changes: 16 additions & 16 deletions mypyc/irbuild/classdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None:
builder.add(InitStatic(non_ext_class, cdef.name, builder.module_name, NAMESPACE_TYPE))

# Add the non-extension class to the dict
builder.primitive_op(dict_set_item_op,
[
builder.load_globals_dict(),
builder.load_static_unicode(cdef.name),
non_ext_class
], cdef.line)
builder.call_c(dict_set_item_op,
[
builder.load_globals_dict(),
builder.load_static_unicode(cdef.name),
non_ext_class
], cdef.line)

# Cache any cachable class attributes
cache_class_attrs(builder, attrs_to_cache, cdef)
Expand Down Expand Up @@ -191,12 +191,12 @@ 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.primitive_op(dict_set_item_op,
[
builder.load_globals_dict(),
builder.load_static_unicode(cdef.name),
tp,
], cdef.line)
builder.call_c(dict_set_item_op,
[
builder.load_globals_dict(),
builder.load_static_unicode(cdef.name),
tp,
], cdef.line)

return tp

Expand Down Expand Up @@ -280,7 +280,7 @@ def add_non_ext_class_attr(builder: IRBuilder,
# TODO: Maybe generate more precise types for annotations
key = builder.load_static_unicode(lvalue.name)
typ = builder.primitive_op(type_object_op, [], stmt.line)
builder.primitive_op(dict_set_item_op, [non_ext.anns, key, typ], stmt.line)
builder.call_c(dict_set_item_op, [non_ext.anns, key, typ], stmt.line)

# Only add the attribute to the __dict__ if the assignment is of the form:
# x: type = value (don't add attributes of the form 'x: type' to the __dict__).
Expand Down Expand Up @@ -470,9 +470,9 @@ def create_mypyc_attrs_tuple(builder: IRBuilder, ir: ClassIR, line: int) -> Valu

def finish_non_ext_dict(builder: IRBuilder, non_ext: NonExtClassInfo, line: int) -> None:
# Add __annotations__ to the class dict.
builder.primitive_op(dict_set_item_op,
[non_ext.dict, builder.load_static_unicode('__annotations__'),
non_ext.anns], -1)
builder.call_c(dict_set_item_op,
[non_ext.dict, builder.load_static_unicode('__annotations__'),
non_ext.anns], -1)

# We add a __doc__ attribute so if the non-extension class is decorated with the
# dataclass decorator, dataclass will not try to look for __text_signature__.
Expand Down
12 changes: 6 additions & 6 deletions mypyc/irbuild/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
)
from mypyc.ir.rtypes import RTuple, object_rprimitive, is_none_rprimitive
from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD
from mypyc.primitives.registry import name_ref_ops
from mypyc.primitives.registry import name_ref_ops, CFunctionDescription
from mypyc.primitives.generic_ops import iter_op
from mypyc.primitives.misc_ops import new_slice_op, ellipsis_op, type_op
from mypyc.primitives.list_ops import new_list_op, list_append_op, list_extend_op
Expand Down Expand Up @@ -491,8 +491,8 @@ def transform_set_expr(builder: IRBuilder, expr: SetExpr) -> Value:
def _visit_display(builder: IRBuilder,
items: List[Expression],
constructor_op: OpDescription,
append_op: OpDescription,
extend_op: OpDescription,
append_op: CFunctionDescription,
extend_op: CFunctionDescription,
line: int
) -> Value:
accepted_items = []
Expand All @@ -512,7 +512,7 @@ def _visit_display(builder: IRBuilder,
if result is None:
result = builder.primitive_op(constructor_op, initial_items, line)

builder.primitive_op(extend_op if starred else append_op, [result, value], line)
builder.call_c(extend_op if starred else append_op, [result, value], line)

if result is None:
result = builder.primitive_op(constructor_op, initial_items, line)
Expand All @@ -534,7 +534,7 @@ def transform_set_comprehension(builder: IRBuilder, o: SetComprehension) -> Valu

def gen_inner_stmts() -> None:
e = builder.accept(gen.left_expr)
builder.primitive_op(set_add_op, [set_ops, e], o.line)
builder.call_c(set_add_op, [set_ops, e], o.line)

comprehension_helper(builder, loop_params, gen_inner_stmts, o.line)
return set_ops
Expand All @@ -547,7 +547,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.primitive_op(dict_set_item_op, [d, k, v], o.line)
builder.call_c(dict_set_item_op, [d, k, v], o.line)

comprehension_helper(builder, loop_params, gen_inner_stmts, o.line)
return d
Expand Down
19 changes: 10 additions & 9 deletions mypyc/irbuild/for_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
)
from mypyc.ir.ops import (
Value, BasicBlock, LoadInt, Branch, Register, AssignmentTarget, TupleGet,
AssignmentTargetTuple, TupleSet, OpDescription, BinaryIntOp
AssignmentTargetTuple, TupleSet, BinaryIntOp
)
from mypyc.ir.rtypes import (
RType, is_short_int_rprimitive, is_list_rprimitive, is_sequence_rprimitive,
RTuple, is_dict_rprimitive, short_int_rprimitive
)
from mypyc.primitives.registry import CFunctionDescription
from mypyc.primitives.dict_ops import (
dict_next_key_op, dict_next_value_op, dict_next_item_op, dict_check_size_op,
dict_key_iter_op, dict_value_iter_op, dict_item_iter_op
Expand Down Expand Up @@ -92,7 +93,7 @@ def translate_list_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Valu

def gen_inner_stmts() -> None:
e = builder.accept(gen.left_expr)
builder.primitive_op(list_append_op, [list_ops, e], gen.line)
builder.call_c(list_append_op, [list_ops, e], gen.line)

comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line)
return list_ops
Expand Down Expand Up @@ -485,8 +486,8 @@ class ForDictionaryCommon(ForGenerator):
since they may override some iteration methods in subtly incompatible manner.
The fallback logic is implemented in CPy.h via dynamic type check.
"""
dict_next_op = None # type: ClassVar[OpDescription]
dict_iter_op = None # type: ClassVar[OpDescription]
dict_next_op = None # type: ClassVar[CFunctionDescription]
dict_iter_op = None # type: ClassVar[CFunctionDescription]

def need_cleanup(self) -> bool:
# Technically, a dict subclass can raise an unrelated exception
Expand All @@ -504,14 +505,14 @@ def init(self, expr_reg: Value, target_type: RType) -> None:
self.size = builder.maybe_spill(self.load_len(self.expr_target))

# For dict class (not a subclass) this is the dictionary itself.
iter_reg = builder.primitive_op(self.dict_iter_op, [expr_reg], self.line)
iter_reg = builder.call_c(self.dict_iter_op, [expr_reg], self.line)
self.iter_target = builder.maybe_spill(iter_reg)

def gen_condition(self) -> None:
"""Get next key/value pair, set new offset, and check if we should continue."""
builder = self.builder
line = self.line
self.next_tuple = self.builder.primitive_op(
self.next_tuple = self.builder.call_c(
self.dict_next_op, [builder.read(self.iter_target, line),
builder.read(self.offset_target, line)], line)

Expand All @@ -532,9 +533,9 @@ def gen_step(self) -> None:
builder = self.builder
line = self.line
# Technically, we don't need a new primitive for this, but it is simpler.
builder.primitive_op(dict_check_size_op,
[builder.read(self.expr_target, line),
builder.read(self.size, line)], line)
builder.call_c(dict_check_size_op,
[builder.read(self.expr_target, line),
builder.read(self.size, line)], line)

def gen_cleanup(self) -> None:
# Same as for generic ForIterable.
Expand Down
8 changes: 4 additions & 4 deletions mypyc/irbuild/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ def transform_decorator(builder: IRBuilder, dec: Decorator) -> None:
decorated_func = load_decorated_func(builder, dec.func, orig_func)

# Set the callable object representing the decorated function as a global.
builder.primitive_op(dict_set_item_op,
[builder.load_globals_dict(),
builder.load_static_unicode(dec.func.name), decorated_func],
decorated_func.line)
builder.call_c(dict_set_item_op,
[builder.load_globals_dict(),
builder.load_static_unicode(dec.func.name), decorated_func],
decorated_func.line)

builder.functions.append(func_ir)

Expand Down
4 changes: 2 additions & 2 deletions mypyc/irbuild/ll_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ def py_call(self,
# don't have an extend method.
pos_args_list = self.primitive_op(new_list_op, pos_arg_values, line)
for star_arg_value in star_arg_values:
self.primitive_op(list_extend_op, [pos_args_list, star_arg_value], line)
self.call_c(list_extend_op, [pos_args_list, star_arg_value], line)
pos_args_tuple = self.call_c(list_tuple_op, [pos_args_list], line)

kw_args_dict = self.make_dict(kw_arg_key_value_pairs, line)
Expand Down Expand Up @@ -591,7 +591,7 @@ def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value:
if result is None:
result = self._create_dict(keys, values, line)

self.primitive_op(
self.call_c(
dict_update_in_display_op,
[result, value],
line=line
Expand Down
6 changes: 3 additions & 3 deletions mypyc/irbuild/specialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ def dict_methods_fast_path(
# Note that it is not safe to use fast methods on dict subclasses, so
# the corresponding helpers in CPy.h fallback to (inlined) generic logic.
if attr == 'keys':
return builder.primitive_op(dict_keys_op, [obj], expr.line)
return builder.call_c(dict_keys_op, [obj], expr.line)
elif attr == 'values':
return builder.primitive_op(dict_values_op, [obj], expr.line)
return builder.call_c(dict_values_op, [obj], expr.line)
else:
return builder.primitive_op(dict_items_op, [obj], expr.line)
return builder.call_c(dict_items_op, [obj], expr.line)


@specialize_function('builtins.tuple')
Expand Down
4 changes: 2 additions & 2 deletions mypyc/irbuild/statement.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ def transform_import(builder: IRBuilder, node: Import) -> None:

# Python 3.7 has a nice 'PyImport_GetModule' function that we can't use :(
mod_dict = builder.primitive_op(get_module_dict_op, [], node.line)
obj = builder.primitive_op(dict_get_item_op,
[mod_dict, builder.load_static_unicode(base)], node.line)
obj = builder.call_c(dict_get_item_op,
[mod_dict, builder.load_static_unicode(base)], node.line)
builder.gen_method_call(
globals, '__setitem__', [builder.load_static_unicode(name), obj],
result_type=None, line=node.line)
Expand Down
Loading

0 comments on commit b1f5121

Please sign in to comment.