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] Use a native unboxed representation for floats #14880

Merged
merged 56 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
5620f68
[mypyc] Use an unboxed representation for floats
JukkaL Dec 28, 2021
0ea696d
Fix after rebase
JukkaL Dec 31, 2022
3d5f397
Support undefined floats in locals
JukkaL Aug 27, 2022
8954cc9
Support float default args
JukkaL Aug 27, 2022
62abec1
WIP mixed operations test case
JukkaL Aug 31, 2022
c8214d0
Support mixed float/int arithmetic
JukkaL Sep 10, 2022
f222c1f
Add run tests
JukkaL Sep 10, 2022
80ebe04
Add primitive for true divide of int by int (resulting in a float)
JukkaL Sep 10, 2022
8ae9c98
Make float(f) a no-op for float arguments
JukkaL Sep 10, 2022
9d874ce
Fix overflow handling when converting to int
JukkaL Sep 10, 2022
916cda9
WIP float divide by zero test case
JukkaL Sep 10, 2022
3bbc76a
Add some irchecking of int and float ops
JukkaL Sep 11, 2022
c208066
Implement float division and modulus
JukkaL Sep 11, 2022
218def2
Add and improve some math primitives
JukkaL Sep 11, 2022
de5dd64
Improve run tests
JukkaL Sep 11, 2022
c1cdda1
Make +x a no-op for floats
JukkaL Sep 14, 2022
47acc06
Fix float negation
JukkaL Sep 14, 2022
8dbd05a
More tests
JukkaL Sep 24, 2022
fc5cd16
Fix float abs
JukkaL Sep 24, 2022
0b2a642
Add more test cases
JukkaL Sep 24, 2022
1cb15ca
Add primitive for math.exp
JukkaL Sep 24, 2022
987d93e
Add primitive for math.floor
JukkaL Sep 24, 2022
1054d4a
Add primitive for math.ceil
JukkaL Sep 24, 2022
f54e618
Add primitive for math.fabs
JukkaL Sep 24, 2022
d7e044e
Add primitive for math.log
JukkaL Sep 24, 2022
2d3b7a3
Add primitive for math.tan and math primiive run tests
JukkaL Sep 24, 2022
289bc29
Add more float values to test
JukkaL Sep 24, 2022
2de8e0b
Black
JukkaL Sep 24, 2022
2141f4d
Add primitive for float floor division
JukkaL Sep 24, 2022
c98c043
Refactor test cases
JukkaL Sep 24, 2022
40e991a
Add primitive for math.pow
JukkaL Sep 24, 2022
fd117e6
Refactor test case
JukkaL Sep 24, 2022
d721828
Add docstrings
JukkaL Sep 24, 2022
6205767
Fix test cases
JukkaL Dec 31, 2022
9a4db2d
Fix math.pow on Python 3.11
JukkaL Dec 31, 2022
4c89337
Update float to native int conversion tests
JukkaL Jan 27, 2023
71d8568
Update i64 test case
JukkaL Jan 27, 2023
1462b9f
Update test case on Python 3.11 (not sure why this changed)
JukkaL Jan 27, 2023
0d710b1
isort and lint
JukkaL Jan 27, 2023
4b4b7a1
Test coercing from bool to float
JukkaL Jan 28, 2023
72db9fc
Test floats in properties, glue methods and traits
JukkaL Jan 28, 2023
4a4376c
Update test case
JukkaL Mar 4, 2023
bdca8ab
Disallow narrowing a float value to int
JukkaL Mar 11, 2023
cc2aedf
Fix type check
JukkaL Mar 11, 2023
937df67
Add test case
JukkaL Mar 11, 2023
e8e694f
Fix test case
JukkaL Mar 11, 2023
00fd468
Add test case
JukkaL Mar 11, 2023
01f670b
Fix test cases to not assign ints to float variables
JukkaL Mar 11, 2023
6cd75b8
Update test case
JukkaL Mar 11, 2023
6384c03
Add i64 to float conversion test case
JukkaL Mar 11, 2023
a177197
Fix tests on 32-bit architecture
JukkaL Mar 11, 2023
3c5931d
Add missing error check to property getter wrappers
JukkaL Mar 11, 2023
8ab5e25
Work around mypyc bug
JukkaL Mar 11, 2023
e85f057
Work around another issue
JukkaL Mar 11, 2023
e45dba5
Fix test on 3.11
JukkaL Mar 12, 2023
c9d7147
Add subnormal values
JukkaL Mar 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions mypy/constant_fold.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non
value = constant_fold_expr(expr.expr, cur_mod_id)
if isinstance(value, int):
return constant_fold_unary_int_op(expr.op, value)
if isinstance(value, float):
return constant_fold_unary_float_op(expr.op, value)
return None


Expand Down Expand Up @@ -110,6 +112,14 @@ def constant_fold_unary_int_op(op: str, value: int) -> int | None:
return None


def constant_fold_unary_float_op(op: str, value: float) -> float | None:
if op == "-":
return -value
elif op == "+":
return value
return None


def constant_fold_binary_str_op(op: str, left: str, right: str) -> str | None:
if op == "+":
return left + right
Expand Down
17 changes: 15 additions & 2 deletions mypyc/analysis/dataflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
ComparisonOp,
ControlOp,
Extend,
Float,
FloatComparisonOp,
FloatNeg,
FloatOp,
GetAttr,
GetElementPtr,
Goto,
Expand Down Expand Up @@ -245,9 +249,18 @@ def visit_load_global(self, op: LoadGlobal) -> GenAndKill[T]:
def visit_int_op(self, op: IntOp) -> GenAndKill[T]:
return self.visit_register_op(op)

def visit_float_op(self, op: FloatOp) -> GenAndKill[T]:
return self.visit_register_op(op)

def visit_float_neg(self, op: FloatNeg) -> GenAndKill[T]:
return self.visit_register_op(op)

def visit_comparison_op(self, op: ComparisonOp) -> GenAndKill[T]:
return self.visit_register_op(op)

def visit_float_comparison_op(self, op: FloatComparisonOp) -> GenAndKill[T]:
return self.visit_register_op(op)

def visit_load_mem(self, op: LoadMem) -> GenAndKill[T]:
return self.visit_register_op(op)

Expand Down Expand Up @@ -444,7 +457,7 @@ def analyze_undefined_regs(
def non_trivial_sources(op: Op) -> set[Value]:
result = set()
for source in op.sources():
if not isinstance(source, Integer):
if not isinstance(source, (Integer, Float)):
result.add(source)
return result

Expand All @@ -454,7 +467,7 @@ def visit_branch(self, op: Branch) -> GenAndKill[Value]:
return non_trivial_sources(op), set()

def visit_return(self, op: Return) -> GenAndKill[Value]:
if not isinstance(op.value, Integer):
if not isinstance(op.value, (Integer, Float)):
return {op.value}, set()
else:
return set(), set()
Expand Down
29 changes: 28 additions & 1 deletion mypyc/analysis/ircheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
ControlOp,
DecRef,
Extend,
FloatComparisonOp,
FloatNeg,
FloatOp,
GetAttr,
GetElementPtr,
Goto,
Expand Down Expand Up @@ -43,6 +46,7 @@
TupleSet,
Unbox,
Unreachable,
Value,
)
from mypyc.ir.pprint import format_func
from mypyc.ir.rtypes import (
Expand All @@ -54,6 +58,7 @@
bytes_rprimitive,
dict_rprimitive,
int_rprimitive,
is_float_rprimitive,
is_object_rprimitive,
list_rprimitive,
range_rprimitive,
Expand Down Expand Up @@ -221,6 +226,14 @@ def check_compatibility(self, op: Op, t: RType, s: RType) -> None:
if not can_coerce_to(t, s) or not can_coerce_to(s, t):
self.fail(source=op, desc=f"{t.name} and {s.name} are not compatible")

def expect_float(self, op: Op, v: Value) -> None:
if not is_float_rprimitive(v.type):
self.fail(op, f"Float expected (actual type is {v.type})")

def expect_non_float(self, op: Op, v: Value) -> None:
if is_float_rprimitive(v.type):
self.fail(op, "Float not expected")

def visit_goto(self, op: Goto) -> None:
self.check_control_op_targets(op)

Expand Down Expand Up @@ -376,10 +389,24 @@ def visit_load_global(self, op: LoadGlobal) -> None:
pass

def visit_int_op(self, op: IntOp) -> None:
pass
self.expect_non_float(op, op.lhs)
self.expect_non_float(op, op.rhs)

def visit_comparison_op(self, op: ComparisonOp) -> None:
self.check_compatibility(op, op.lhs.type, op.rhs.type)
self.expect_non_float(op, op.lhs)
self.expect_non_float(op, op.rhs)

def visit_float_op(self, op: FloatOp) -> None:
self.expect_float(op, op.lhs)
self.expect_float(op, op.rhs)

def visit_float_neg(self, op: FloatNeg) -> None:
self.expect_float(op, op.src)

def visit_float_comparison_op(self, op: FloatComparisonOp) -> None:
self.expect_float(op, op.lhs)
self.expect_float(op, op.rhs)

def visit_load_mem(self, op: LoadMem) -> None:
pass
Expand Down
12 changes: 12 additions & 0 deletions mypyc/analysis/selfleaks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
Cast,
ComparisonOp,
Extend,
FloatComparisonOp,
FloatNeg,
FloatOp,
GetAttr,
GetElementPtr,
Goto,
Expand Down Expand Up @@ -160,6 +163,15 @@ def visit_int_op(self, op: IntOp) -> GenAndKill:
def visit_comparison_op(self, op: ComparisonOp) -> GenAndKill:
return CLEAN

def visit_float_op(self, op: FloatOp) -> GenAndKill:
return CLEAN

def visit_float_neg(self, op: FloatNeg) -> GenAndKill:
return CLEAN

def visit_float_comparison_op(self, op: FloatComparisonOp) -> GenAndKill:
return CLEAN

def visit_load_mem(self, op: LoadMem) -> GenAndKill:
return CLEAN

Expand Down
12 changes: 12 additions & 0 deletions mypyc/codegen/emit.py
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,16 @@ def emit_unbox(
self.emit_line(f"{dest} = CPyLong_AsInt32({src});")
# TODO: Handle 'optional'
# TODO: Handle 'failure'
elif is_float_rprimitive(typ):
if declare_dest:
self.emit_line("double {};".format(dest))
# TODO: Don't use __float__ and __index__
self.emit_line(f"{dest} = PyFloat_AsDouble({src});")
self.emit_lines(
f"if ({dest} == -1.0 && PyErr_Occurred()) {{", f"{dest} = -113.0;", "}"
)
# TODO: Handle 'optional'
# TODO: Handle 'failure'
elif isinstance(typ, RTuple):
self.declare_tuple_struct(typ)
if declare_dest:
Expand Down Expand Up @@ -983,6 +993,8 @@ def emit_box(
self.emit_line(f"{declaration}{dest} = PyLong_FromLong({src});")
elif is_int64_rprimitive(typ):
self.emit_line(f"{declaration}{dest} = PyLong_FromLongLong({src});")
elif is_float_rprimitive(typ):
self.emit_line(f"{declaration}{dest} = PyFloat_FromDouble({src});")
elif isinstance(typ, RTuple):
self.declare_tuple_struct(typ)
self.emit_line(f"{declaration}{dest} = PyTuple_New({len(typ.types)});")
Expand Down
1 change: 1 addition & 0 deletions mypyc/codegen/emitclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,7 @@ def generate_readonly_getter(
emitter.ctype_spaced(rtype), NATIVE_PREFIX, func_ir.cname(emitter.names)
)
)
emitter.emit_error_check("retval", rtype, "return NULL;")
emitter.emit_box("retval", "retbox", rtype, declare_dest=True)
emitter.emit_line("return retbox;")
else:
Expand Down
32 changes: 32 additions & 0 deletions mypyc/codegen/emitfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
ComparisonOp,
DecRef,
Extend,
Float,
FloatComparisonOp,
FloatNeg,
FloatOp,
GetAttr,
GetElementPtr,
Goto,
Expand Down Expand Up @@ -671,6 +675,27 @@ def visit_comparison_op(self, op: ComparisonOp) -> None:
lhs_cast = self.emit_signed_int_cast(op.lhs.type)
self.emit_line(f"{dest} = {lhs_cast}{lhs} {op.op_str[op.op]} {rhs_cast}{rhs};")

def visit_float_op(self, op: FloatOp) -> None:
dest = self.reg(op)
lhs = self.reg(op.lhs)
rhs = self.reg(op.rhs)
if op.op != FloatOp.MOD:
self.emit_line("%s = %s %s %s;" % (dest, lhs, op.op_str[op.op], rhs))
else:
# TODO: This may set errno as a side effect, that is a little sketchy.
self.emit_line("%s = fmod(%s, %s);" % (dest, lhs, rhs))

def visit_float_neg(self, op: FloatNeg) -> None:
dest = self.reg(op)
src = self.reg(op.src)
self.emit_line(f"{dest} = -{src};")

def visit_float_comparison_op(self, op: FloatComparisonOp) -> None:
dest = self.reg(op)
lhs = self.reg(op.lhs)
rhs = self.reg(op.rhs)
self.emit_line("%s = %s %s %s;" % (dest, lhs, op.op_str[op.op], rhs))

def visit_load_mem(self, op: LoadMem) -> None:
dest = self.reg(op)
src = self.reg(op.src)
Expand Down Expand Up @@ -732,6 +757,13 @@ def reg(self, reg: Value) -> str:
elif val <= -(1 << 31):
s += "LL"
return s
elif isinstance(reg, Float):
r = repr(reg.value)
if r == "inf":
return "INFINITY"
elif r == "-inf":
return "-INFINITY"
return r
else:
return self.emitter.reg(reg)

Expand Down
1 change: 1 addition & 0 deletions mypyc/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"getargs.c",
"getargsfast.c",
"int_ops.c",
"float_ops.c",
"str_ops.c",
"bytes_ops.c",
"list_ops.c",
Expand Down
Loading