Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mypyc] Faster bool and integer conversions #14422

Merged
merged 13 commits into from
Jan 11, 2023
Next Next commit
[mypyc] Optimize conversions between int/bool
Previously we used slow generic operations for these.
  • Loading branch information
JukkaL committed Jan 10, 2023
commit b7a0e38738c0ad0c653e2a242c2259e2f8a84fb1
7 changes: 6 additions & 1 deletion mypyc/irbuild/ll_builder.py
Original file line number Diff line number Diff line change
@@ -326,6 +326,11 @@ def coerce(
):
# Equivalent types
return src
elif is_bool_rprimitive(src_type) and is_int_rprimitive(target_type):
shifted = self.int_op(
bool_rprimitive, src, Integer(1, bool_rprimitive), IntOp.LEFT_SHIFT
)
return self.add(Extend(shifted, int_rprimitive, signed=False))
else:
# To go from one unboxed type to another, we go through a boxed
# in-between value, for simplicity.
@@ -1795,7 +1800,7 @@ def matching_call_c(
return target
return None

def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value:
def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int = -1) -> Value:
"""Generate a native integer binary op.

Use native/C semantics, which sometimes differ from Python
31 changes: 31 additions & 0 deletions mypyc/irbuild/specialize.py
Original file line number Diff line number Diff line change
@@ -33,8 +33,12 @@
)
from mypy.types import AnyType, TypeOfAny
from mypyc.ir.ops import (
Assign,
BasicBlock,
Branch,
ComparisonOp,
Extend,
Goto,
Integer,
RaiseStandardError,
Register,
@@ -51,6 +55,7 @@
dict_rprimitive,
int32_rprimitive,
int64_rprimitive,
int_rprimitive,
is_dict_rprimitive,
is_int32_rprimitive,
is_int64_rprimitive,
@@ -688,3 +693,29 @@ def translate_i32(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value
val = builder.accept(arg)
return builder.coerce(val, int32_rprimitive, expr.line)
return None


@specialize_function("builtins.bool")
def translate_bool(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None:
if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS:
return None
arg = expr.args[0]
arg_type = builder.node_type(arg)
if is_int_rprimitive(arg_type):
src = builder.accept(arg)
tmp = Register(bool_rprimitive)
b1, b2, b3 = BasicBlock(), BasicBlock(), BasicBlock()
chk = builder.builder.comparison_op(
src, Integer(0, int_rprimitive), ComparisonOp.EQ, expr.line
)
builder.flush_keep_alives()
builder.add(Branch(chk, b1, b2, Branch.BOOL))
builder.activate_block(b1)
builder.add(Assign(tmp, Integer(0, bool_rprimitive)))
builder.add(Goto(b3))
builder.activate_block(b2)
builder.add(Assign(tmp, Integer(1, bool_rprimitive)))
builder.add(Goto(b3))
builder.activate_block(b3)
return tmp
return None
28 changes: 28 additions & 0 deletions mypyc/test-data/irbuild-int.test
Original file line number Diff line number Diff line change
@@ -203,3 +203,31 @@ L0:
def f5():
L0:
return -2

[case testBoolToAndFromInt]
def b2i(b: bool) -> int:
return b
def i2b(n: int) -> bool:
return bool(n)
[out]
def b2i(b):
b, r0 :: bool
r1 :: int
L0:
r0 = b << 1
r1 = extend r0: builtins.bool to builtins.int
return r1
def i2b(n):
n :: int
r0 :: bit
r1 :: bool
L0:
r0 = n == 0
if r0 goto L1 else goto L2 :: bool
L1:
r1 = 0
goto L3
L2:
r1 = 1
L3:
return r1
12 changes: 12 additions & 0 deletions mypyc/test-data/run-integers.test
Original file line number Diff line number Diff line change
@@ -353,6 +353,9 @@ def is_true(x: int) -> bool:
else:
return False

def is_true2(x: int) -> bool:
return bool(x)

def is_false(x: int) -> bool:
if not x:
return True
@@ -361,11 +364,20 @@ def is_false(x: int) -> bool:

def test_int_as_bool() -> None:
assert not is_true(0)
assert not is_true2(0)
assert is_false(0)
for x in 1, 55, -1, -7, 1 << 50, 1 << 101, -(1 << 50), -(1 << 101):
assert is_true(x)
assert is_true2(x)
assert not is_false(x)

def bool_as_int(b: bool) -> int:
return b

def test_bool_as_int() -> None:
assert bool_as_int(False) == 0
assert bool_as_int(True) == 1

def test_divide() -> None:
for x in range(-100, 100):
for y in range(-100, 100):