Skip to content

Commit c4f55ae

Browse files
authored
[mypyc] Inline math literals (#15324)
This PR inlines math literals such as `math.pi` and `math.e`. Previously Using `math.pi` would emit a getattr, unbox, error check, then box op code (if we need a boxed value), but now it just generates a literal directly (that is sometimes boxed). Closes mypyc/mypyc#985
1 parent c0af000 commit c4f55ae

File tree

8 files changed

+118
-0
lines changed

8 files changed

+118
-0
lines changed

mypyc/codegen/emitfunc.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,8 @@ def reg(self, reg: Value) -> str:
775775
return "INFINITY"
776776
elif r == "-inf":
777777
return "-INFINITY"
778+
elif r == "nan":
779+
return "NAN"
778780
return r
779781
else:
780782
return self.emitter.reg(reg)

mypyc/codegen/literals.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ def float_to_c(x: float) -> str:
265265
return "INFINITY"
266266
elif s == "-inf":
267267
return "-INFINITY"
268+
elif s == "nan":
269+
return "NAN"
268270
return s
269271

270272

mypyc/irbuild/builder.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ def load_bytes_from_str_literal(self, value: str) -> Value:
301301
def load_int(self, value: int) -> Value:
302302
return self.builder.load_int(value)
303303

304+
def load_float(self, value: float) -> Value:
305+
return self.builder.load_float(value)
306+
304307
def unary_op(self, lreg: Value, expr_op: str, line: int) -> Value:
305308
return self.builder.unary_op(lreg, expr_op, line)
306309

mypyc/irbuild/expression.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from __future__ import annotations
88

9+
import math
910
from typing import Callable, Sequence
1011

1112
from mypy.nodes import (
@@ -128,6 +129,10 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value:
128129
if fullname == "builtins.False":
129130
return builder.false()
130131

132+
math_literal = transform_math_literal(builder, fullname)
133+
if math_literal is not None:
134+
return math_literal
135+
131136
if isinstance(expr.node, Var) and expr.node.is_final:
132137
value = builder.emit_load_final(
133138
expr.node,
@@ -192,6 +197,10 @@ def transform_member_expr(builder: IRBuilder, expr: MemberExpr) -> Value:
192197
if value is not None:
193198
return value
194199

200+
math_literal = transform_math_literal(builder, expr.fullname)
201+
if math_literal is not None:
202+
return math_literal
203+
195204
if isinstance(expr.node, MypyFile) and expr.node.fullname in builder.imports:
196205
return builder.load_module(expr.node.fullname)
197206

@@ -1043,3 +1052,18 @@ def transform_assignment_expr(builder: IRBuilder, o: AssignmentExpr) -> Value:
10431052
target = builder.get_assignment_target(o.target)
10441053
builder.assign(target, value, o.line)
10451054
return value
1055+
1056+
1057+
def transform_math_literal(builder: IRBuilder, fullname: str) -> Value | None:
1058+
if fullname == "math.e":
1059+
return builder.load_float(math.e)
1060+
if fullname == "math.pi":
1061+
return builder.load_float(math.pi)
1062+
if fullname == "math.inf":
1063+
return builder.load_float(math.inf)
1064+
if fullname == "math.nan":
1065+
return builder.load_float(math.nan)
1066+
if fullname == "math.tau":
1067+
return builder.load_float(math.tau)
1068+
1069+
return None

mypyc/test-data/irbuild-math.test

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
[case testMathLiteralsAreInlined]
2+
import math
3+
from math import pi, e, tau, inf, nan
4+
5+
def f1() -> float:
6+
return pi
7+
8+
def f2() -> float:
9+
return math.pi
10+
11+
def f3() -> float:
12+
return math.e
13+
14+
def f4() -> float:
15+
return math.e
16+
17+
def f5() -> float:
18+
return math.tau
19+
20+
def f6() -> float:
21+
return math.tau
22+
23+
def f7() -> float:
24+
return math.inf
25+
def f8() -> float:
26+
return math.inf
27+
28+
def f9() -> float:
29+
return math.nan
30+
31+
def f10() -> float:
32+
return math.nan
33+
34+
[out]
35+
def f1():
36+
L0:
37+
return 3.141592653589793
38+
def f2():
39+
L0:
40+
return 3.141592653589793
41+
def f3():
42+
L0:
43+
return 2.718281828459045
44+
def f4():
45+
L0:
46+
return 2.718281828459045
47+
def f5():
48+
L0:
49+
return 6.283185307179586
50+
def f6():
51+
L0:
52+
return 6.283185307179586
53+
def f7():
54+
L0:
55+
return inf
56+
def f8():
57+
L0:
58+
return inf
59+
def f9():
60+
L0:
61+
return nan
62+
def f10():
63+
L0:
64+
return nan

mypyc/test-data/run-math.test

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Any, Callable
55
from typing_extensions import Final
66
import math
7+
from math import pi, e, tau, inf, nan
78
from testutil import assertRaises, float_vals, assertDomainError, assertMathRangeError
89

910
pymath: Any = math
@@ -86,3 +87,20 @@ def test_isinf() -> None:
8687
def test_isnan() -> None:
8788
for x in float_vals:
8889
assert repr(math.isnan(x)) == repr(pymath.isnan(x))
90+
91+
92+
def test_pi_is_inlined_correctly() -> None:
93+
assert math.pi == pi == 3.141592653589793
94+
95+
def test_e_is_inlined_correctly() -> None:
96+
assert math.e == e == 2.718281828459045
97+
98+
def test_tau_is_inlined_correctly() -> None:
99+
assert math.tau == tau == 6.283185307179586
100+
101+
def test_inf_is_inlined_correctly() -> None:
102+
assert math.inf == inf == float("inf")
103+
104+
def test_nan_is_inlined_correctly() -> None:
105+
assert math.isnan(math.nan)
106+
assert math.isnan(nan)

mypyc/test/test_irbuild.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"irbuild-singledispatch.test",
5050
"irbuild-constant-fold.test",
5151
"irbuild-glue-methods.test",
52+
"irbuild-math.test",
5253
]
5354

5455
if sys.version_info >= (3, 10):

test-data/unit/lib-stub/math.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
pi: float
2+
e: float
3+
tau: float
4+
inf: float
5+
nan: float
26
def sqrt(__x: float) -> float: ...
37
def sin(__x: float) -> float: ...
48
def cos(__x: float) -> float: ...

0 commit comments

Comments
 (0)