-
-
Notifications
You must be signed in to change notification settings - Fork 3k
[mypyc] Constant fold int operations and str concat #11194
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
Changes from all commits
3337117
e1d0dc4
10506e1
f32ca57
795030d
c36166b
fab3e93
04cde5d
d9fe463
8a3b8c4
873cf05
c0c76aa
632eb2b
927aab2
a7bfc58
3afec3b
98e46a1
35940d5
6bff050
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
"""Constant folding of IR values. | ||
|
||
For example, 3 + 5 can be constant folded into 8. | ||
""" | ||
|
||
from typing import Optional, Union | ||
from typing_extensions import Final | ||
|
||
from mypy.nodes import Expression, IntExpr, StrExpr, OpExpr, UnaryExpr, NameExpr, MemberExpr, Var | ||
from mypyc.irbuild.builder import IRBuilder | ||
|
||
|
||
# All possible result types of constant folding | ||
ConstantValue = Union[int, str] | ||
CONST_TYPES: Final = (int, str) | ||
|
||
|
||
def constant_fold_expr(builder: IRBuilder, expr: Expression) -> Optional[ConstantValue]: | ||
"""Return the constant value of an expression for supported operations. | ||
|
||
Return None otherwise. | ||
""" | ||
if isinstance(expr, IntExpr): | ||
return expr.value | ||
if isinstance(expr, StrExpr): | ||
return expr.value | ||
elif isinstance(expr, NameExpr): | ||
node = expr.node | ||
if isinstance(node, Var) and node.is_final: | ||
value = node.final_value | ||
if isinstance(value, (CONST_TYPES)): | ||
return value | ||
elif isinstance(expr, MemberExpr): | ||
final = builder.get_final_ref(expr) | ||
if final is not None: | ||
fn, final_var, native = final | ||
if final_var.is_final: | ||
value = final_var.final_value | ||
if isinstance(value, (CONST_TYPES)): | ||
return value | ||
elif isinstance(expr, OpExpr): | ||
left = constant_fold_expr(builder, expr.left) | ||
right = constant_fold_expr(builder, expr.right) | ||
if isinstance(left, int) and isinstance(right, int): | ||
return constant_fold_binary_int_op(expr.op, left, right) | ||
elif isinstance(left, str) and isinstance(right, str): | ||
return constant_fold_binary_str_op(expr.op, left, right) | ||
elif isinstance(expr, UnaryExpr): | ||
value = constant_fold_expr(builder, expr.expr) | ||
if isinstance(value, int): | ||
return constant_fold_unary_int_op(expr.op, value) | ||
return None | ||
|
||
|
||
def constant_fold_binary_int_op(op: str, left: int, right: int) -> Optional[int]: | ||
if op == '+': | ||
return left + right | ||
if op == '-': | ||
return left - right | ||
elif op == '*': | ||
return left * right | ||
elif op == '//': | ||
if right != 0: | ||
return left // right | ||
elif op == '%': | ||
if right != 0: | ||
return left % right | ||
elif op == '&': | ||
return left & right | ||
elif op == '|': | ||
return left | right | ||
elif op == '^': | ||
return left ^ right | ||
elif op == '<<': | ||
if right >= 0: | ||
return left << right | ||
elif op == '>>': | ||
if right >= 0: | ||
return left >> right | ||
elif op == '**': | ||
if right >= 0: | ||
return left ** right | ||
return None | ||
|
||
|
||
def constant_fold_unary_int_op(op: str, value: int) -> Optional[int]: | ||
if op == '-': | ||
return -value | ||
elif op == '~': | ||
return ~value | ||
elif op == '+': | ||
return value | ||
return None | ||
|
||
|
||
def constant_fold_binary_str_op(op: str, left: str, right: str) -> Optional[str]: | ||
if op == '+': | ||
return left + right | ||
return None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A random thought: add support for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that's a good idea. We already have an item about this (and a few other things) in the issue mypyc/mypyc#772. My intention is to add support for this and a few more cases in follow-up PRs (e.g. float arithmetic). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this file be in the
transform
folder?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put the file here since
transform
contains IR-to-IR tranforms, and this is basically an AST analysis pass, which seems closer to what is happening inirbuild
overall.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good, then I don't have anything else blocking this from merging.