Skip to content

Commit 6ee562a

Browse files
authored
[mypyc] new error_kind and branch variant to handle call_negative_bool_emit (#9035)
Related: mypyc/mypyc#734 and mypyc/mypyc#741. Introducing ERR_NEG_INT error_kind and NEG_INT_EXPR branch variant to support checking the return value of a call is non-negative. set.discard is used as an example. With some modifications, this would also support negative_int_emit.
1 parent 95eff27 commit 6ee562a

File tree

15 files changed

+69
-37
lines changed

15 files changed

+69
-37
lines changed

mypyc/codegen/emitfunc.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC,
1414
NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal
1515
)
16-
from mypyc.ir.rtypes import RType, RTuple, is_c_int_rprimitive
16+
from mypyc.ir.rtypes import RType, RTuple, is_int32_rprimitive, is_int64_rprimitive
1717
from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD
1818
from mypyc.ir.class_ir import ClassIR
1919

@@ -105,6 +105,9 @@ def visit_branch(self, op: Branch) -> None:
105105
if op.op == Branch.BOOL_EXPR:
106106
expr_result = self.reg(op.left) # right isn't used
107107
cond = '{}{}'.format(neg, expr_result)
108+
elif op.op == Branch.NEG_INT_EXPR:
109+
expr_result = self.reg(op.left)
110+
cond = '{} < 0'.format(expr_result)
108111
elif op.op == Branch.IS_ERROR:
109112
typ = op.left.type
110113
compare = '!=' if op.negated else '=='
@@ -179,7 +182,7 @@ def visit_assign(self, op: Assign) -> None:
179182

180183
def visit_load_int(self, op: LoadInt) -> None:
181184
dest = self.reg(op)
182-
if is_c_int_rprimitive(op.type):
185+
if is_int32_rprimitive(op.type) or is_int64_rprimitive(op.type):
183186
self.emit_line('%s = %d;' % (dest, op.value))
184187
else:
185188
self.emit_line('%s = %d;' % (dest, op.value * 2))

mypyc/common.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
from typing import Dict, Any
23

34
from typing_extensions import Final
@@ -28,6 +29,8 @@
2829
# Maximal number of subclasses for a class to trigger fast path in isinstance() checks.
2930
FAST_ISINSTANCE_MAX_SUBCLASSES = 2 # type: Final
3031

32+
IS_32_BIT_PLATFORM = sys.maxsize < (1 << 31) # type: Final
33+
3134

3235
def decorator_helper_name(func_name: str) -> str:
3336
return '__mypyc_{}_decorator_helper__'.format(func_name)

mypyc/ir/ops.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ def terminated(self) -> bool:
293293
ERR_MAGIC = 1 # type: Final
294294
# Generates false (bool) on exception
295295
ERR_FALSE = 2 # type: Final
296+
# Generates negative integer on exception
297+
ERR_NEG_INT = 3 # type: Final
296298

297299
# Hack: using this line number for an op will suppress it in tracebacks
298300
NO_TRACEBACK_LINE_NO = -10000
@@ -413,10 +415,12 @@ class Branch(ControlOp):
413415

414416
BOOL_EXPR = 100 # type: Final
415417
IS_ERROR = 101 # type: Final
418+
NEG_INT_EXPR = 102 # type: Final
416419

417420
op_names = {
418421
BOOL_EXPR: ('%r', 'bool'),
419422
IS_ERROR: ('is_error(%r)', ''),
423+
NEG_INT_EXPR: ('%r < 0', ''),
420424
} # type: Final
421425

422426
def __init__(self,

mypyc/ir/rtypes.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
from typing_extensions import Final, ClassVar, TYPE_CHECKING
2727

28-
from mypyc.common import JsonDict, short_name
28+
from mypyc.common import JsonDict, short_name, IS_32_BIT_PLATFORM
2929
from mypyc.namegen import NameGenerator
3030

3131
if TYPE_CHECKING:
@@ -174,7 +174,9 @@ def __init__(self,
174174
self.is_unboxed = is_unboxed
175175
self._ctype = ctype
176176
self.is_refcounted = is_refcounted
177-
if ctype in ('CPyTagged', 'Py_ssize_t'):
177+
# TODO: For low-level integers, they actually don't have undefined values
178+
# we need to figure out some way to represent here.
179+
if ctype in ('CPyTagged', 'int32_t', 'int64_t'):
178180
self.c_undefined = 'CPY_INT_TAG'
179181
elif ctype == 'PyObject *':
180182
# Boxed types use the null pointer as the error value.
@@ -234,9 +236,17 @@ def __repr__(self) -> str:
234236
short_int_rprimitive = RPrimitive('short_int', is_unboxed=True, is_refcounted=False,
235237
ctype='CPyTagged') # type: Final
236238

237-
# low level integer (corresponds to C's 'int').
238-
c_int_rprimitive = RPrimitive('c_int', is_unboxed=True, is_refcounted=False,
239-
ctype='Py_ssize_t') # type: Final
239+
# low level integer (corresponds to C's 'int's).
240+
int32_rprimitive = RPrimitive('int32', is_unboxed=True, is_refcounted=False,
241+
ctype='int32_t') # type: Final
242+
int64_rprimitive = RPrimitive('int64', is_unboxed=True, is_refcounted=False,
243+
ctype='int64_t') # type: Final
244+
# integer alias
245+
c_int_rprimitive = int32_rprimitive
246+
if IS_32_BIT_PLATFORM:
247+
c_pyssize_t_rprimitive = int32_rprimitive
248+
else:
249+
c_pyssize_t_rprimitive = int64_rprimitive
240250

241251
# Floats are represent as 'float' PyObject * values. (In the future
242252
# we'll likely switch to a more efficient, unboxed representation.)
@@ -279,8 +289,12 @@ def is_short_int_rprimitive(rtype: RType) -> bool:
279289
return rtype is short_int_rprimitive
280290

281291

282-
def is_c_int_rprimitive(rtype: RType) -> bool:
283-
return rtype is c_int_rprimitive
292+
def is_int32_rprimitive(rtype: RType) -> bool:
293+
return rtype is int32_rprimitive
294+
295+
296+
def is_int64_rprimitive(rtype: RType) -> bool:
297+
return rtype is int64_rprimitive
284298

285299

286300
def is_float_rprimitive(rtype: RType) -> bool:

mypyc/irbuild/ll_builder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from mypyc.ir.rtypes import (
2727
RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive,
2828
bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive,
29-
c_int_rprimitive
29+
c_pyssize_t_rprimitive
3030
)
3131
from mypyc.ir.func_ir import FuncDecl, FuncSignature
3232
from mypyc.ir.class_ir import ClassIR, all_concrete_classes
@@ -884,7 +884,7 @@ def _create_dict(self,
884884
# keys and values should have the same number of items
885885
size = len(keys)
886886
if size > 0:
887-
load_size_op = self.add(LoadInt(size, -1, c_int_rprimitive))
887+
load_size_op = self.add(LoadInt(size, -1, c_pyssize_t_rprimitive))
888888
# merge keys and values
889889
items = [i for t in list(zip(keys, values)) for i in t]
890890
return self.call_c(dict_build_op, [load_size_op] + items, line)

mypyc/primitives/dict_ops.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from mypyc.ir.ops import EmitterInterface, ERR_FALSE, ERR_MAGIC, ERR_NEVER
66
from mypyc.ir.rtypes import (
77
dict_rprimitive, object_rprimitive, bool_rprimitive, int_rprimitive,
8-
list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_int_rprimitive
8+
list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_pyssize_t_rprimitive
99
)
1010

1111
from mypyc.primitives.registry import (
@@ -99,7 +99,7 @@
9999
# Positional argument is the number of key-value pairs
100100
# Variable arguments are (key1, value1, ..., keyN, valueN).
101101
dict_build_op = c_custom_op(
102-
arg_types=[c_int_rprimitive],
102+
arg_types=[c_pyssize_t_rprimitive],
103103
return_type=dict_rprimitive,
104104
c_function_name='CPyDict_Build',
105105
error_kind=ERR_MAGIC,

mypyc/primitives/set_ops.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
func_op, method_op, binary_op, simple_emit, negative_int_emit,
55
call_negative_bool_emit, c_function_op, c_method_op
66
)
7-
from mypyc.ir.ops import ERR_MAGIC, ERR_FALSE, ERR_NEVER, EmitterInterface
8-
from mypyc.ir.rtypes import object_rprimitive, bool_rprimitive, set_rprimitive, int_rprimitive
7+
from mypyc.ir.ops import ERR_MAGIC, ERR_FALSE, ERR_NEVER, ERR_NEG_INT, EmitterInterface
8+
from mypyc.ir.rtypes import (
9+
object_rprimitive, bool_rprimitive, set_rprimitive, int_rprimitive, c_int_rprimitive
10+
)
911
from typing import List
1012

1113

@@ -70,13 +72,12 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None:
7072
error_kind=ERR_FALSE)
7173

7274
# set.discard(obj)
73-
method_op(
75+
c_method_op(
7476
name='discard',
7577
arg_types=[set_rprimitive, object_rprimitive],
76-
result_type=bool_rprimitive,
77-
error_kind=ERR_FALSE,
78-
emit=call_negative_bool_emit('PySet_Discard')
79-
)
78+
return_type=c_int_rprimitive,
79+
c_function_name='PySet_Discard',
80+
error_kind=ERR_NEG_INT)
8081

8182
# set.add(obj)
8283
set_add_op = method_op(

mypyc/test-data/irbuild-basic.test

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,7 @@ def call_python_function_with_keyword_arg(x):
10881088
r1 :: object
10891089
r2 :: str
10901090
r3 :: tuple
1091-
r4 :: c_int
1091+
r4 :: native_int
10921092
r5 :: object
10931093
r6 :: dict
10941094
r7 :: object
@@ -1113,7 +1113,7 @@ def call_python_method_with_keyword_args(xs, first, second):
11131113
r3 :: str
11141114
r4 :: object
11151115
r5 :: tuple
1116-
r6 :: c_int
1116+
r6 :: native_int
11171117
r7 :: object
11181118
r8 :: dict
11191119
r9 :: object
@@ -1122,7 +1122,7 @@ def call_python_method_with_keyword_args(xs, first, second):
11221122
r12 :: object
11231123
r13, r14 :: str
11241124
r15 :: tuple
1125-
r16 :: c_int
1125+
r16 :: native_int
11261126
r17, r18 :: object
11271127
r19 :: dict
11281128
r20 :: object
@@ -1690,7 +1690,7 @@ def g():
16901690
r3 :: short_int
16911691
r4 :: str
16921692
r5 :: short_int
1693-
r6 :: c_int
1693+
r6 :: native_int
16941694
r7, r8, r9 :: object
16951695
r10, r11 :: dict
16961696
r12 :: str
@@ -1727,7 +1727,7 @@ def h():
17271727
r2 :: short_int
17281728
r3 :: str
17291729
r4 :: short_int
1730-
r5 :: c_int
1730+
r5 :: native_int
17311731
r6, r7 :: object
17321732
r8, r9 :: dict
17331733
r10 :: str

mypyc/test-data/irbuild-dict.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def f(x):
5858
x :: object
5959
r0, r1 :: short_int
6060
r2 :: str
61-
r3 :: c_int
61+
r3 :: native_int
6262
r4, r5 :: object
6363
r6, d :: dict
6464
r7 :: None
@@ -204,7 +204,7 @@ def f(x, y):
204204
r0 :: short_int
205205
r1 :: str
206206
r2 :: short_int
207-
r3 :: c_int
207+
r3 :: native_int
208208
r4 :: object
209209
r5 :: dict
210210
r6 :: bool

mypyc/test-data/irbuild-set.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,14 @@ def f():
141141
r0, x :: set
142142
r1 :: short_int
143143
r2 :: object
144-
r3 :: bool
144+
r3 :: int32
145145
r4 :: None
146146
L0:
147147
r0 = set
148148
x = r0
149149
r1 = 1
150150
r2 = box(short_int, r1)
151-
r3 = x.discard(r2) :: set
151+
r3 = PySet_Discard(x, r2)
152152
r4 = None
153153
return x
154154

mypyc/test-data/irbuild-statements.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ def delDict():
717717
r1 :: short_int
718718
r2 :: str
719719
r3 :: short_int
720-
r4 :: c_int
720+
r4 :: native_int
721721
r5, r6 :: object
722722
r7, d :: dict
723723
r8 :: str
@@ -746,7 +746,7 @@ def delDictMultiple():
746746
r5 :: short_int
747747
r6 :: str
748748
r7 :: short_int
749-
r8 :: c_int
749+
r8 :: native_int
750750
r9, r10, r11, r12 :: object
751751
r13, d :: dict
752752
r14, r15 :: str

mypyc/test-data/refcount.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ def g(x):
740740
r1 :: object
741741
r2 :: str
742742
r3 :: tuple
743-
r4 :: c_int
743+
r4 :: native_int
744744
r5 :: object
745745
r6 :: dict
746746
r7 :: object

mypyc/test/test_irbuild.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from mypy.test.data import DataDrivenTestCase
77
from mypy.errors import CompileError
88

9-
from mypyc.common import TOP_LEVEL_NAME
9+
from mypyc.common import TOP_LEVEL_NAME, IS_32_BIT_PLATFORM
1010
from mypyc.ir.func_ir import format_func
1111
from mypyc.test.testutil import (
1212
ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file,
@@ -42,7 +42,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
4242
"""Perform a runtime checking transformation test case."""
4343
with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase):
4444
expected_output = remove_comment_lines(testcase.output)
45-
45+
# replace native_int with platform specific ints
46+
int_format_str = 'int32' if IS_32_BIT_PLATFORM else 'int64'
47+
expected_output = [s.replace('native_int', int_format_str) for s in expected_output]
4648
try:
4749
ir = build_ir_for_single_file(testcase.input, options)
4850
except CompileError as e:

mypyc/test/test_refcount.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from mypy.test.data import DataDrivenTestCase
1111
from mypy.errors import CompileError
1212

13-
from mypyc.common import TOP_LEVEL_NAME
13+
from mypyc.common import TOP_LEVEL_NAME, IS_32_BIT_PLATFORM
1414
from mypyc.ir.func_ir import format_func
1515
from mypyc.transform.refcount import insert_ref_count_opcodes
1616
from mypyc.test.testutil import (
@@ -32,7 +32,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
3232
"""Perform a runtime checking transformation test case."""
3333
with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase):
3434
expected_output = remove_comment_lines(testcase.output)
35-
35+
# replace native_int with platform specific ints
36+
int_format_str = 'int32' if IS_32_BIT_PLATFORM else 'int64'
37+
expected_output = [s.replace('native_int', int_format_str) for s in expected_output]
3638
try:
3739
ir = build_ir_for_single_file(testcase.input)
3840
except CompileError as e:

mypyc/transform/exceptions.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from mypyc.ir.ops import (
1515
BasicBlock, LoadErrorValue, Return, Branch, RegisterOp, ERR_NEVER, ERR_MAGIC,
16-
ERR_FALSE, NO_TRACEBACK_LINE_NO,
16+
ERR_FALSE, ERR_NEG_INT, NO_TRACEBACK_LINE_NO,
1717
)
1818
from mypyc.ir.func_ir import FuncIR
1919

@@ -74,6 +74,9 @@ def split_blocks_at_errors(blocks: List[BasicBlock],
7474
# Op returns a C false value on error.
7575
variant = Branch.BOOL_EXPR
7676
negated = True
77+
elif op.error_kind == ERR_NEG_INT:
78+
variant = Branch.NEG_INT_EXPR
79+
negated = False
7780
else:
7881
assert False, 'unknown error kind %d' % op.error_kind
7982

0 commit comments

Comments
 (0)