Skip to content

Commit ffd9d1c

Browse files
authored
[mypyc] Introduce GetElementPtr (python#9260)
Related to mypyc/mypyc#741. This PR introduces GetElementPtr, which computes the address of an element in an aggregate type (currently RStruct is supported). Part of efforts to support the len and other macro-related primitives.
1 parent 7938f5d commit ffd9d1c

File tree

4 files changed

+55
-6
lines changed

4 files changed

+55
-6
lines changed

mypyc/analysis/dataflow.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
BasicBlock, OpVisitor, Assign, LoadInt, LoadErrorValue, RegisterOp, Goto, Branch, Return, Call,
1010
Environment, Box, Unbox, Cast, Op, Unreachable, TupleGet, TupleSet, GetAttr, SetAttr,
1111
LoadStatic, InitStatic, PrimitiveOp, MethodCall, RaiseStandardError, CallC, LoadGlobal,
12-
Truncate, BinaryIntOp, LoadMem
12+
Truncate, BinaryIntOp, LoadMem, GetElementPtr
1313
)
1414

1515

@@ -211,6 +211,9 @@ def visit_binary_int_op(self, op: BinaryIntOp) -> GenAndKill:
211211
def visit_load_mem(self, op: LoadMem) -> GenAndKill:
212212
return self.visit_register_op(op)
213213

214+
def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill:
215+
return self.visit_register_op(op)
216+
214217

215218
class DefinedVisitor(BaseAnalysisVisitor):
216219
"""Visitor for finding defined registers.

mypyc/codegen/emitfunc.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
LoadStatic, InitStatic, TupleGet, TupleSet, Call, IncRef, DecRef, Box, Cast, Unbox,
1313
BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC,
1414
NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal, Truncate,
15-
BinaryIntOp, LoadMem
15+
BinaryIntOp, LoadMem, GetElementPtr
1616
)
1717
from mypyc.ir.rtypes import (
18-
RType, RTuple, is_tagged, is_int32_rprimitive, is_int64_rprimitive
18+
RType, RTuple, is_tagged, is_int32_rprimitive, is_int64_rprimitive, RStruct
1919
)
2020
from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD
2121
from mypyc.ir.class_ir import ClassIR
@@ -471,6 +471,14 @@ def visit_load_mem(self, op: LoadMem) -> None:
471471
type = self.ctype(op.type)
472472
self.emit_line('%s = *(%s *)%s;' % (dest, type, src))
473473

474+
def visit_get_element_ptr(self, op: GetElementPtr) -> None:
475+
dest = self.reg(op)
476+
src = self.reg(op.src)
477+
# TODO: support tuple type
478+
assert isinstance(op.src_type, RStruct)
479+
assert op.field in op.src_type.names, "Invalid field name."
480+
self.emit_line('%s = &%s.%s;' % (dest, src, op.field))
481+
474482
# Helpers
475483

476484
def label(self, label: BasicBlock) -> str:

mypyc/ir/ops.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
from mypyc.ir.rtypes import (
2626
RType, RInstance, RTuple, RVoid, is_bool_rprimitive, is_int_rprimitive,
2727
is_short_int_rprimitive, is_none_rprimitive, object_rprimitive, bool_rprimitive,
28-
short_int_rprimitive, int_rprimitive, void_rtype, is_c_py_ssize_t_rprimitive
28+
short_int_rprimitive, int_rprimitive, void_rtype, is_c_py_ssize_t_rprimitive,
29+
c_pyssize_t_rprimitive
2930
)
3031
from mypyc.common import short_name
3132

@@ -1372,6 +1373,28 @@ def accept(self, visitor: 'OpVisitor[T]') -> T:
13721373
return visitor.visit_load_mem(self)
13731374

13741375

1376+
class GetElementPtr(RegisterOp):
1377+
"""Get the address of a struct element"""
1378+
error_kind = ERR_NEVER
1379+
1380+
def __init__(self, src: Value, src_type: RType, field: str, line: int = -1) -> None:
1381+
super().__init__(line)
1382+
self.type = c_pyssize_t_rprimitive
1383+
self.src = src
1384+
self.src_type = src_type
1385+
self.field = field
1386+
1387+
def sources(self) -> List[Value]:
1388+
return [self.src]
1389+
1390+
def to_str(self, env: Environment) -> str:
1391+
return env.format("%r = get_element_ptr %r %r :: %r", self, self.src,
1392+
self.field, self.src_type)
1393+
1394+
def accept(self, visitor: 'OpVisitor[T]') -> T:
1395+
return visitor.visit_get_element_ptr(self)
1396+
1397+
13751398
@trait
13761399
class OpVisitor(Generic[T]):
13771400
"""Generic visitor over ops (uses the visitor design pattern)."""
@@ -1482,6 +1505,10 @@ def visit_binary_int_op(self, op: BinaryIntOp) -> T:
14821505
def visit_load_mem(self, op: LoadMem) -> T:
14831506
raise NotImplementedError
14841507

1508+
@abstractmethod
1509+
def visit_get_element_ptr(self, op: GetElementPtr) -> T:
1510+
raise NotImplementedError
1511+
14851512

14861513
# TODO: Should this live somewhere else?
14871514
LiteralsMap = Dict[Tuple[Type[object], Union[int, float, str, bytes, complex]], str]

mypyc/test/test_emitfunc.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
from mypyc.ir.ops import (
1111
Environment, BasicBlock, Goto, Return, LoadInt, Assign, IncRef, DecRef, Branch,
1212
Call, Unbox, Box, TupleGet, GetAttr, PrimitiveOp, RegisterOp,
13-
SetAttr, Op, Value, CallC, BinaryIntOp, LoadMem
13+
SetAttr, Op, Value, CallC, BinaryIntOp, LoadMem, GetElementPtr
1414
)
1515
from mypyc.ir.rtypes import (
1616
RTuple, RInstance, int_rprimitive, bool_rprimitive, list_rprimitive,
1717
dict_rprimitive, object_rprimitive, c_int_rprimitive, short_int_rprimitive, int32_rprimitive,
18-
int64_rprimitive
18+
int64_rprimitive, StructInfo, RStruct
1919
)
2020
from mypyc.ir.func_ir import FuncIR, FuncDecl, RuntimeArg, FuncSignature
2121
from mypyc.ir.class_ir import ClassIR
@@ -282,6 +282,17 @@ def test_load_mem(self) -> None:
282282
self.assert_emit(LoadMem(bool_rprimitive, self.i64),
283283
"""cpy_r_r0 = *(char *)cpy_r_i64;""")
284284

285+
def test_get_element_ptr(self) -> None:
286+
info = StructInfo("Foo", ["b", "i32", "i64"], [bool_rprimitive,
287+
int32_rprimitive, int64_rprimitive])
288+
r = RStruct(info)
289+
self.assert_emit(GetElementPtr(self.o, r, "b"),
290+
"""cpy_r_r0 = &cpy_r_o.b;""")
291+
self.assert_emit(GetElementPtr(self.o, r, "i32"),
292+
"""cpy_r_r00 = &cpy_r_o.i32;""")
293+
self.assert_emit(GetElementPtr(self.o, r, "i64"),
294+
"""cpy_r_r01 = &cpy_r_o.i64;""")
295+
285296
def assert_emit(self, op: Op, expected: str) -> None:
286297
self.emitter.fragments = []
287298
self.declarations.fragments = []

0 commit comments

Comments
 (0)