Skip to content

Commit 3e9dd3c

Browse files
authored
[mypyc] Basic test-only support for i32 and i64 (#13018)
Basic support for arithmetic, bitwise and comparison ops using native int types i32 and i64. Also support some implicit coercions between int and native int types. There are still various gaps in the functionality, and that's why the feature is test-only so far. Division and modulus ops require jumping through some hoops to make the behavior with negative operands match Python semantics. This can already show some improvements in benchmarks (using hacks these can be used outside tests). I'll post figures once the implementation is more complete. Work on mypyc/mypyc#837.
1 parent 0406826 commit 3e9dd3c

18 files changed

+3019
-43
lines changed

mypy/meet.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
131131
if declared.type.alt_promote:
132132
# Special case: low-level integer type can't be narrowed
133133
return original_declared
134+
if (
135+
isinstance(narrowed, Instance)
136+
and narrowed.type.alt_promote
137+
and narrowed.type.alt_promote is declared.type
138+
):
139+
# Special case: 'int' can't be narrowed down to a native int type such as
140+
# i64, since they have different runtime representations.
141+
return original_declared
134142
return meet_types(original_declared, original_narrowed)
135143
elif isinstance(declared, (TupleType, TypeType, LiteralType)):
136144
return meet_types(original_declared, original_narrowed)

mypyc/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343

4444
# Maximum value for a short tagged integer.
4545
MAX_SHORT_INT: Final = sys.maxsize >> 1
46+
# Minimum value for a short tagged integer.
47+
MIN_SHORT_INT: Final = -(sys.maxsize >> 1) - 1
4648

4749
# Maximum value for a short tagged integer represented as a C integer literal.
4850
#

mypyc/irbuild/ast_helpers.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
Var,
2222
)
2323
from mypyc.ir.ops import BasicBlock
24-
from mypyc.ir.rtypes import is_tagged
24+
from mypyc.ir.rtypes import is_fixed_width_rtype, is_tagged
2525
from mypyc.irbuild.builder import IRBuilder
2626
from mypyc.irbuild.constant_fold import constant_fold_expr
2727

@@ -70,7 +70,10 @@ def maybe_process_conditional_comparison(
7070
return False
7171
ltype = self.node_type(e.operands[0])
7272
rtype = self.node_type(e.operands[1])
73-
if not is_tagged(ltype) or not is_tagged(rtype):
73+
if not (
74+
(is_tagged(ltype) or is_fixed_width_rtype(ltype))
75+
and (is_tagged(rtype) or is_fixed_width_rtype(rtype))
76+
):
7477
return False
7578
op = e.operators[0]
7679
if op not in ("==", "!=", "<", "<=", ">", ">="):
@@ -80,8 +83,17 @@ def maybe_process_conditional_comparison(
8083
borrow_left = is_borrow_friendly_expr(self, right_expr)
8184
left = self.accept(left_expr, can_borrow=borrow_left)
8285
right = self.accept(right_expr, can_borrow=True)
83-
# "left op right" for two tagged integers
84-
self.builder.compare_tagged_condition(left, right, op, true, false, e.line)
86+
if is_fixed_width_rtype(ltype) or is_fixed_width_rtype(rtype):
87+
if not is_fixed_width_rtype(ltype):
88+
left = self.coerce(left, rtype, e.line)
89+
elif not is_fixed_width_rtype(rtype):
90+
right = self.coerce(right, ltype, e.line)
91+
reg = self.binary_op(left, right, op, e.line)
92+
self.builder.flush_keep_alives()
93+
self.add_bool_branch(reg, true, false)
94+
else:
95+
# "left op right" for two tagged integers
96+
self.builder.compare_tagged_condition(left, right, op, true, false, e.line)
8597
return True
8698

8799

mypyc/irbuild/expression.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
from mypyc.ir.ops import (
5353
Assign,
5454
BasicBlock,
55+
ComparisonOp,
56+
Integer,
5557
LoadAddress,
5658
RaiseStandardError,
5759
Register,
@@ -62,6 +64,7 @@
6264
from mypyc.ir.rtypes import (
6365
RTuple,
6466
int_rprimitive,
67+
is_fixed_width_rtype,
6568
is_int_rprimitive,
6669
is_list_rprimitive,
6770
is_none_rprimitive,
@@ -472,21 +475,26 @@ def transform_op_expr(builder: IRBuilder, expr: OpExpr) -> Value:
472475
if folded:
473476
return folded
474477

478+
borrow_left = False
479+
borrow_right = False
480+
481+
ltype = builder.node_type(expr.left)
482+
rtype = builder.node_type(expr.right)
483+
475484
# Special case some int ops to allow borrowing operands.
476-
if is_int_rprimitive(builder.node_type(expr.left)) and is_int_rprimitive(
477-
builder.node_type(expr.right)
478-
):
485+
if is_int_rprimitive(ltype) and is_int_rprimitive(rtype):
479486
if expr.op == "//":
480487
expr = try_optimize_int_floor_divide(expr)
481488
if expr.op in int_borrow_friendly_op:
482489
borrow_left = is_borrow_friendly_expr(builder, expr.right)
483-
left = builder.accept(expr.left, can_borrow=borrow_left)
484-
right = builder.accept(expr.right, can_borrow=True)
485-
return builder.binary_op(left, right, expr.op, expr.line)
490+
borrow_right = True
491+
elif is_fixed_width_rtype(ltype) and is_fixed_width_rtype(rtype):
492+
borrow_left = is_borrow_friendly_expr(builder, expr.right)
493+
borrow_right = True
486494

487-
return builder.binary_op(
488-
builder.accept(expr.left), builder.accept(expr.right), expr.op, expr.line
489-
)
495+
left = builder.accept(expr.left, can_borrow=borrow_left)
496+
right = builder.accept(expr.right, can_borrow=borrow_right)
497+
return builder.binary_op(left, right, expr.op, expr.line)
490498

491499

492500
def try_optimize_int_floor_divide(expr: OpExpr) -> OpExpr:
@@ -708,6 +716,25 @@ def transform_basic_comparison(
708716
and op in int_comparison_op_mapping.keys()
709717
):
710718
return builder.compare_tagged(left, right, op, line)
719+
if is_fixed_width_rtype(left.type) and op in int_comparison_op_mapping.keys():
720+
if right.type == left.type:
721+
op_id = ComparisonOp.signed_ops[op]
722+
return builder.builder.comparison_op(left, right, op_id, line)
723+
elif isinstance(right, Integer):
724+
op_id = ComparisonOp.signed_ops[op]
725+
return builder.builder.comparison_op(
726+
left, Integer(right.value >> 1, left.type), op_id, line
727+
)
728+
elif (
729+
is_fixed_width_rtype(right.type)
730+
and op in int_comparison_op_mapping.keys()
731+
and isinstance(left, Integer)
732+
):
733+
op_id = ComparisonOp.signed_ops[op]
734+
return builder.builder.comparison_op(
735+
Integer(left.value >> 1, right.type), right, op_id, line
736+
)
737+
711738
negate = False
712739
if op == "is not":
713740
op, negate = "is", True

mypyc/irbuild/for_helpers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
bool_rprimitive,
3939
int_rprimitive,
4040
is_dict_rprimitive,
41+
is_fixed_width_rtype,
4142
is_list_rprimitive,
4243
is_sequence_rprimitive,
4344
is_short_int_rprimitive,
@@ -887,7 +888,9 @@ def init(self, start_reg: Value, end_reg: Value, step: int) -> None:
887888
self.step = step
888889
self.end_target = builder.maybe_spill(end_reg)
889890
if is_short_int_rprimitive(start_reg.type) and is_short_int_rprimitive(end_reg.type):
890-
index_type = short_int_rprimitive
891+
index_type: RType = short_int_rprimitive
892+
elif is_fixed_width_rtype(end_reg.type):
893+
index_type = end_reg.type
891894
else:
892895
index_type = int_rprimitive
893896
index_reg = Register(index_type)

0 commit comments

Comments
 (0)