Skip to content

Commit 34dc670

Browse files
authored
[mypyc] Merge yield_from_except_op (#9660)
relates mypyc/mypyc#753 This PR merges yield_from_except_op into the new IR, including several changes: A change to C wrapper's signature since the new IR currently has no unified way to represent the address of a pointer. A change to LoadAddress, allowing it to load local reg's address. A change to uninit pass, suppressing checks on LoadAddress
1 parent 6bd40b8 commit 34dc670

File tree

6 files changed

+50
-23
lines changed

6 files changed

+50
-23
lines changed

mypyc/codegen/emitfunc.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
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, GetElementPtr, LoadAddress, ComparisonOp, SetMem
15+
BinaryIntOp, LoadMem, GetElementPtr, LoadAddress, ComparisonOp, SetMem, Register
1616
)
1717
from mypyc.ir.rtypes import (
1818
RType, RTuple, is_tagged, is_int32_rprimitive, is_int64_rprimitive, RStruct,
@@ -496,7 +496,8 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None:
496496
def visit_load_address(self, op: LoadAddress) -> None:
497497
typ = op.type
498498
dest = self.reg(op)
499-
self.emit_line('%s = (%s)&%s;' % (dest, typ._ctype, op.src))
499+
src = self.reg(op.src) if isinstance(op.src, Register) else op.src
500+
self.emit_line('%s = (%s)&%s;' % (dest, typ._ctype, src))
500501

501502
# Helpers
502503

mypyc/ir/ops.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,19 +1502,34 @@ def accept(self, visitor: 'OpVisitor[T]') -> T:
15021502

15031503

15041504
class LoadAddress(RegisterOp):
1505+
"""Get the address of a value
1506+
1507+
ret = (type)&src
1508+
1509+
Attributes:
1510+
type: Type of the loaded address(e.g. ptr/object_ptr)
1511+
src: Source value, str for named constants like 'PyList_Type',
1512+
Register for temporary values
1513+
"""
15051514
error_kind = ERR_NEVER
15061515
is_borrowed = True
15071516

1508-
def __init__(self, type: RType, src: str, line: int = -1) -> None:
1517+
def __init__(self, type: RType, src: Union[str, Register], line: int = -1) -> None:
15091518
super().__init__(line)
15101519
self.type = type
15111520
self.src = src
15121521

15131522
def sources(self) -> List[Value]:
1514-
return []
1523+
if isinstance(self.src, Register):
1524+
return [self.src]
1525+
else:
1526+
return []
15151527

15161528
def to_str(self, env: Environment) -> str:
1517-
return env.format("%r = load_address %s", self, self.src)
1529+
if isinstance(self.src, Register):
1530+
return env.format("%r = load_address %r", self, self.src)
1531+
else:
1532+
return env.format("%r = load_address %s", self, self.src)
15181533

15191534
def accept(self, visitor: 'OpVisitor[T]') -> T:
15201535
return visitor.visit_load_address(self)

mypyc/ir/rtypes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ def __init__(self,
191191
self.c_undefined = 'NULL'
192192
elif ctype == 'char':
193193
self.c_undefined = '2'
194+
elif ctype == 'PyObject **':
195+
self.c_undefined = 'NULL'
194196
else:
195197
assert False, 'Unrecognized ctype: %r' % ctype
196198

@@ -223,6 +225,10 @@ def __repr__(self) -> str:
223225
object_rprimitive = RPrimitive('builtins.object', is_unboxed=False,
224226
is_refcounted=True) # type: Final
225227

228+
# represents a low level pointer of an object
229+
object_pointer_rprimitive = RPrimitive('object_ptr', is_unboxed=False,
230+
is_refcounted=False, ctype='PyObject **') # type: Final
231+
226232
# Arbitrary-precision integer (corresponds to Python 'int'). Small
227233
# enough values are stored unboxed, while large integers are
228234
# represented as a tagged pointer to a Python 'int' PyObject. The

mypyc/irbuild/function.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020

2121
from mypyc.ir.ops import (
2222
BasicBlock, Value, Return, SetAttr, LoadInt, Environment, GetAttr, Branch, AssignmentTarget,
23-
TupleGet, InitStatic
23+
InitStatic, LoadAddress
2424
)
25-
from mypyc.ir.rtypes import object_rprimitive, RInstance
25+
from mypyc.ir.rtypes import object_rprimitive, RInstance, object_pointer_rprimitive
2626
from mypyc.ir.func_ir import (
2727
FuncIR, FuncSignature, RuntimeArg, FuncDecl, FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FUNC_NORMAL
2828
)
@@ -523,9 +523,10 @@ def except_body() -> None:
523523
# The body of the except is all implemented in a C function to
524524
# reduce how much code we need to generate. It returns a value
525525
# indicating whether to break or yield (or raise an exception).
526-
res = builder.primitive_op(yield_from_except_op, [builder.read(iter_reg)], o.line)
527-
to_stop = builder.add(TupleGet(res, 0, o.line))
528-
val = builder.add(TupleGet(res, 1, o.line))
526+
val = builder.alloc_temp(object_rprimitive)
527+
val_address = builder.add(LoadAddress(object_pointer_rprimitive, val))
528+
to_stop = builder.call_c(yield_from_except_op,
529+
[builder.read(iter_reg), val_address], o.line)
529530

530531
ok, stop = BasicBlock(), BasicBlock()
531532
builder.add(Branch(to_stop, stop, ok, Branch.BOOL))

mypyc/primitives/misc_ops.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, ERR_FALSE
44
from mypyc.ir.rtypes import (
5-
RTuple, bool_rprimitive, object_rprimitive, str_rprimitive,
5+
bool_rprimitive, object_rprimitive, str_rprimitive, object_pointer_rprimitive,
66
int_rprimitive, dict_rprimitive, c_int_rprimitive, bit_rprimitive
77
)
88
from mypyc.primitives.registry import (
9-
simple_emit, custom_op, c_function_op, c_custom_op, load_address_op, ERR_NEG_INT
9+
c_function_op, c_custom_op, load_address_op, ERR_NEG_INT
1010
)
1111

1212

@@ -55,21 +55,19 @@
5555
error_kind=ERR_NEVER)
5656

5757
# This is sort of unfortunate but oh well: yield_from_except performs most of the
58-
# error handling logic in `yield from` operations. It returns a bool and a value.
58+
# error handling logic in `yield from` operations. It returns a bool and passes
59+
# a value by address.
5960
# If the bool is true, then a StopIteration was received and we should return.
6061
# If the bool is false, then the value should be yielded.
6162
# The normal case is probably that it signals an exception, which gets
6263
# propagated.
63-
yield_from_rtuple = RTuple([bool_rprimitive, object_rprimitive])
64-
6564
# Op used for "yield from" error handling.
6665
# See comment in CPy_YieldFromErrorHandle for more information.
67-
yield_from_except_op = custom_op(
68-
name='yield_from_except',
69-
arg_types=[object_rprimitive],
70-
result_type=yield_from_rtuple,
71-
error_kind=ERR_MAGIC,
72-
emit=simple_emit('{dest}.f0 = CPy_YieldFromErrorHandle({args[0]}, &{dest}.f1);'))
66+
yield_from_except_op = c_custom_op(
67+
arg_types=[object_rprimitive, object_pointer_rprimitive],
68+
return_type=bool_rprimitive,
69+
c_function_name='CPy_YieldFromErrorHandle',
70+
error_kind=ERR_MAGIC)
7371

7472
# Create method object from a callable object and self.
7573
method_new_op = c_custom_op(

mypyc/transform/uninit.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
AnalysisDict
1010
)
1111
from mypyc.ir.ops import (
12-
BasicBlock, Branch, Value, RaiseStandardError, Unreachable, Environment, Register
12+
BasicBlock, Branch, Value, RaiseStandardError, Unreachable, Environment, Register,
13+
LoadAddress
1314
)
1415
from mypyc.ir.func_ir import FuncIR
1516

@@ -44,8 +45,13 @@ def split_blocks_at_uninits(env: Environment,
4445
# If a register operand is not guaranteed to be
4546
# initialized is an operand to something other than a
4647
# check that it is defined, insert a check.
48+
49+
# Note that for register operand in a LoadAddress op,
50+
# we should be able to use it without initialization
51+
# as we may need to use its address to update itself
4752
if (isinstance(src, Register) and src not in defined
48-
and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR)):
53+
and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR)
54+
and not isinstance(op, LoadAddress)):
4955
new_block, error_block = BasicBlock(), BasicBlock()
5056
new_block.error_handler = error_block.error_handler = cur_block.error_handler
5157
new_blocks += [error_block, new_block]

0 commit comments

Comments
 (0)