Skip to content

Commit 9da6043

Browse files
authored
[mypyc] Add irbuild support for vec types (#20724)
Add basic irbuild support for `vec[t]`. The runtime representation of `vec[t]` is a C struct. This is similar to how fixed-length tuples are represented. Multiple different structs are used, depending on the item type (`VecI32` for `vec[i32`] and so on). The C extension `librt.vec` that defines the `vec` type was added in #20653 and #20656. These PRs also explain the implementation in more detail. Add RType subclass RVec that is used for vecs. We need a new RType class, since primitives types can't be generic and they can't be struct types. This is based on an old branch, so it mostly uses old-style primitives. I am planning to modernize some of the primitives in follow-up PRs. This doesn't include codegen support, and irbuild test cases are only included for `vec[i64]`. I will create follow-up PRs that add the remaining irbuild tests, codegen support and run tests. All these tests are are passing on my local full branch. Related issue: mypyc/mypyc#840
1 parent d488cc1 commit 9da6043

32 files changed

+2161
-47
lines changed

mypyc/analysis/capsule_deps.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from __future__ import annotations
22

3+
from mypyc.ir.class_ir import ClassIR
34
from mypyc.ir.deps import Dependency
45
from mypyc.ir.func_ir import FuncIR
56
from mypyc.ir.ops import Assign, CallC, PrimitiveOp
6-
from mypyc.ir.rtypes import RStruct, RTuple, RType, RUnion
7+
from mypyc.ir.rtypes import RStruct, RTuple, RType, RUnion, RVec
78

89

910
def find_implicit_op_dependencies(fn: FuncIR) -> set[Dependency] | None:
@@ -48,6 +49,15 @@ def find_type_dependencies(fn: FuncIR, deps: set[Dependency] | None) -> set[Depe
4849
return deps
4950

5051

52+
def find_class_dependencies(cl: ClassIR) -> set[Dependency] | None:
53+
"""Find dependencies from class attribute types."""
54+
deps: set[Dependency] | None = None
55+
for base in cl.mro:
56+
for attr_type in base.attributes.values():
57+
deps = collect_type_deps(attr_type, deps)
58+
return deps
59+
60+
5161
def collect_type_deps(typ: RType, deps: set[Dependency] | None) -> set[Dependency] | None:
5262
"""Collect dependencies from an RType, recursively checking compound types."""
5363
if typ.dependencies is not None:
@@ -64,4 +74,6 @@ def collect_type_deps(typ: RType, deps: set[Dependency] | None) -> set[Dependenc
6474
elif isinstance(typ, RStruct):
6575
for item in typ.types:
6676
deps = collect_type_deps(item, deps)
77+
elif isinstance(typ, RVec):
78+
deps = collect_type_deps(typ.item_type, deps)
6779
return deps

mypyc/analysis/dataflow.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@ def visit_assign_multi(self, op: AssignMulti) -> GenAndKill[T]:
197197
def visit_set_mem(self, op: SetMem) -> GenAndKill[T]:
198198
raise NotImplementedError
199199

200+
def visit_inc_ref(self, op: IncRef) -> GenAndKill[T]:
201+
return self.visit_register_op(op)
202+
203+
def visit_dec_ref(self, op: DecRef) -> GenAndKill[T]:
204+
return self.visit_register_op(op)
205+
200206
def visit_call(self, op: Call) -> GenAndKill[T]:
201207
return self.visit_register_op(op)
202208

mypyc/analysis/ircheck.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,17 @@
6363
RPrimitive,
6464
RType,
6565
RUnion,
66+
RVec,
6667
bytes_rprimitive,
6768
dict_rprimitive,
6869
int_rprimitive,
70+
is_c_py_ssize_t_rprimitive,
71+
is_fixed_width_rtype,
6972
is_float_rprimitive,
7073
is_object_rprimitive,
74+
is_pointer_rprimitive,
7175
list_rprimitive,
76+
pointer_rprimitive,
7277
range_rprimitive,
7378
set_rprimitive,
7479
str_rprimitive,
@@ -200,7 +205,7 @@ def can_coerce_to(src: RType, dest: RType) -> bool:
200205
if src.name in disjoint_types and dest.name in disjoint_types:
201206
return src.name == dest.name
202207
return src.size == dest.size
203-
if isinstance(src, RInstance):
208+
if isinstance(src, (RInstance, RVec)):
204209
return is_object_rprimitive(dest)
205210
if isinstance(src, RUnion):
206211
# IR doesn't have the ability to narrow unions based on
@@ -211,6 +216,29 @@ def can_coerce_to(src: RType, dest: RType) -> bool:
211216
return True
212217

213218

219+
def is_valid_ptr_displacement_type(rtype: RType) -> bool:
220+
"""Check if rtype is a valid displacement type for pointer arithmetic."""
221+
if not (is_fixed_width_rtype(rtype) or is_c_py_ssize_t_rprimitive(rtype)):
222+
return False
223+
assert isinstance(rtype, RPrimitive)
224+
return rtype.size == pointer_rprimitive.size
225+
226+
227+
def is_pointer_arithmetic(op: IntOp) -> bool:
228+
"""Check if op is add/subtract targeting pointer_rprimitive and integer of the same size."""
229+
if op.op not in (IntOp.ADD, IntOp.SUB):
230+
return False
231+
if not is_pointer_rprimitive(op.type):
232+
return False
233+
left = op.lhs.type
234+
right = op.rhs.type
235+
if is_pointer_rprimitive(left):
236+
return is_valid_ptr_displacement_type(right)
237+
if is_pointer_rprimitive(right):
238+
return is_valid_ptr_displacement_type(left)
239+
return False
240+
241+
214242
class OpChecker(OpVisitor[None]):
215243
def __init__(self, parent_fn: FuncIR) -> None:
216244
self.parent_fn = parent_fn
@@ -417,6 +445,7 @@ def visit_int_op(self, op: IntOp) -> None:
417445
op_str in ("+", "-", "*", "/", "%")
418446
or (op_str not in ("<<", ">>") and left.size != right.size)
419447
)
448+
and not is_pointer_arithmetic(op)
420449
):
421450
self.fail(op, f"Operand types have incompatible signs: {left}, {right}")
422451

mypyc/analysis/selfleaks.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
CallC,
1212
Cast,
1313
ComparisonOp,
14+
DecRef,
1415
Extend,
1516
FloatComparisonOp,
1617
FloatNeg,
@@ -19,6 +20,7 @@
1920
GetElement,
2021
GetElementPtr,
2122
Goto,
23+
IncRef,
2224
InitStatic,
2325
IntOp,
2426
KeepAlive,
@@ -90,6 +92,12 @@ def visit_assign_multi(self, op: AssignMulti) -> GenAndKill:
9092
def visit_set_mem(self, op: SetMem) -> GenAndKill:
9193
return CLEAN
9294

95+
def visit_inc_ref(self, op: IncRef) -> GenAndKill:
96+
return CLEAN
97+
98+
def visit_dec_ref(self, op: DecRef) -> GenAndKill:
99+
return CLEAN
100+
93101
def visit_call(self, op: Call) -> GenAndKill:
94102
fn = op.fn
95103
if fn.class_name and fn.name == "__init__":

mypyc/ir/deps.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ def get_header(self) -> str:
5656
STRING_WRITER_EXTRA_OPS: Final = SourceDep("stringwriter_extra_ops.c")
5757
BYTEARRAY_EXTRA_OPS: Final = SourceDep("bytearray_extra_ops.c")
5858
STR_EXTRA_OPS: Final = SourceDep("str_extra_ops.c")
59+
VECS_EXTRA_OPS: Final = SourceDep("vecs_extra_ops.c")

mypyc/ir/ops.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class to enable the new behavior. Sometimes adding a new abstract
3939
RTuple,
4040
RType,
4141
RUnion,
42+
RVec,
4243
RVoid,
4344
bit_rprimitive,
4445
bool_rprimitive,
@@ -1690,7 +1691,7 @@ class GetElement(RegisterOp):
16901691

16911692
def __init__(self, src: Value, field: str, line: int = -1) -> None:
16921693
super().__init__(line)
1693-
assert isinstance(src.type, RStruct)
1694+
assert isinstance(src.type, (RStruct, RVec))
16941695
self.type = src.type.field_type(field)
16951696
self.src = src
16961697
self.src_type = src.type
@@ -1720,7 +1721,7 @@ class GetElementPtr(RegisterOp):
17201721

17211722
def __init__(self, src: Value, src_type: RType, field: str, line: int = -1) -> None:
17221723
super().__init__(line)
1723-
assert not isinstance(src.type, RStruct)
1724+
assert not isinstance(src.type, (RStruct, RVec))
17241725
self.type = pointer_rprimitive
17251726
self.src = src
17261727
self.src_type = src_type
@@ -1750,7 +1751,7 @@ class SetElement(RegisterOp):
17501751

17511752
def __init__(self, src: Value, field: str, item: Value, line: int = -1) -> None:
17521753
super().__init__(line)
1753-
assert isinstance(src.type, RStruct), src.type
1754+
assert isinstance(src.type, (RStruct, RVec)), src.type
17541755
self.type = src.type
17551756
self.src = src
17561757
self.item = item

0 commit comments

Comments
 (0)