%PDF- %PDF-
| Direktori : /lib/python3/dist-packages/pythran/optimizations/ |
| Current File : //lib/python3/dist-packages/pythran/optimizations/constant_folding.py |
""" ConstantFolding performs some kind of partial evaluation. """
from pythran.analyses import ConstantExpressions, ASTMatcher
from pythran.passmanager import Transformation
from pythran.tables import MODULES
from pythran.conversion import to_ast, ConversionError, ToNotEval, mangle
from pythran.analyses.ast_matcher import DamnTooLongPattern
from pythran.syntax import PythranSyntaxError
from pythran.utils import isintegral, isnum
import gast as ast
from copy import deepcopy
import logging
logger = logging.getLogger('pythran')
class ConstantFolding(Transformation):
"""
Replace constant expression by their evaluation.
>>> import gast as ast
>>> from pythran import passmanager, backend
>>> node = ast.parse("def foo(): return 1+3")
>>> pm = passmanager.PassManager("test")
>>> _, node = pm.apply(ConstantFolding, node)
>>> print(pm.dump(backend.Python, node))
def foo():
return 4
"""
def __init__(self):
Transformation.__init__(self, ConstantExpressions)
def prepare(self, node):
assert isinstance(node, ast.Module)
self.env = {
'builtins': __import__('builtins'),
}
for module_name in MODULES:
# __dispatch__ is the only fake top-level module
if module_name != '__dispatch__':
alias_module_name = mangle(module_name)
try:
self.env[alias_module_name] = __import__(module_name)
except ImportError:
pass
# we need to parse the whole code to be able to apply user-defined pure
# function but import are resolved before so we remove them to avoid
# ImportError (for operator_ for example)
dummy_module = ast.Module([s for s in node.body
if not isinstance(s, ast.Import)],
[])
eval(compile(ast.gast_to_ast(dummy_module),
'<constant_folding>', 'exec'),
self.env)
super(ConstantFolding, self).prepare(node)
def skip(self, node):
return node
visit_Constant = visit_Name = skip
visit_List = visit_Set = Transformation.generic_visit
visit_Dict = visit_Tuple = Transformation.generic_visit
def generic_visit(self, node):
if isinstance(node, ast.expr) and node in self.constant_expressions:
fake_node = ast.Expression(node)
code = compile(ast.gast_to_ast(fake_node),
'<constant folding>', 'eval')
try:
value = eval(code, self.env)
new_node = to_ast(value)
try:
if not ASTMatcher(node).search(new_node):
self.update = True
return new_node
except DamnTooLongPattern as e:
print("W: ", e, " Assume no update happened.")
return Transformation.generic_visit(self, node)
except ConversionError as e:
print('error in constant folding: ', e)
raise
except ToNotEval:
return Transformation.generic_visit(self, node)
except AttributeError as e:
# this may miss a few optimization
logger.info('During constant folding, bailing out due to: ' +
e.args[0])
return Transformation.generic_visit(self, node)
except NameError as e:
# FIXME dispatched function are not processed by constant
# folding
if "__dispatch__" in e.args[0]:
return Transformation.generic_visit(self, node)
# this may miss a few optimization
logger.info('During constant folding, bailing out due to: ' +
e.args[0])
return Transformation.generic_visit(self, node)
except Exception as e:
raise PythranSyntaxError(str(e), node)
else:
return Transformation.generic_visit(self, node)
class PartialConstantFolding(Transformation):
"""
Replace partially constant expression by their evaluation.
>>> import gast as ast
>>> from pythran import passmanager, backend
>>> node = ast.parse("def foo(n): return [n] * 2")
>>> pm = passmanager.PassManager("test")
>>> _, node = pm.apply(PartialConstantFolding, node)
>>> print(pm.dump(backend.Python, node))
def foo(n):
return [n, n]
>>> node = ast.parse("def foo(n): return 2 * (n,)")
>>> _, node = pm.apply(PartialConstantFolding, node)
>>> print(pm.dump(backend.Python, node))
def foo(n):
return (n, n)
>>> node = ast.parse("def foo(n): return [n] + [n]")
>>> _, node = pm.apply(PartialConstantFolding, node)
>>> print(pm.dump(backend.Python, node))
def foo(n):
return [n, n]
>>> node = ast.parse("def foo(n, m): return (n,) + (m, n)")
>>> _, node = pm.apply(PartialConstantFolding, node)
>>> print(pm.dump(backend.Python, node))
def foo(n, m):
return (n, m, n)
"""
def __init__(self):
Transformation.__init__(self, ConstantExpressions)
def fold_mult_left(self, node):
if not isinstance(node.left, (ast.List, ast.Tuple)):
return False
if not isnum(node.right):
return False
# FIXME: remove that check once we have a proper type inference engine
if not isintegral(node.right):
raise PythranSyntaxError("Multiplying a sequence by a float", node)
return isinstance(node.op, ast.Mult)
def fold_mult_right(self, node):
if not isinstance(node.right, (ast.List, ast.Tuple)):
return False
if not isnum(node.left):
return False
# FIXME: remove that check once we have a proper type inference engine
if not isintegral(node.left):
raise PythranSyntaxError("Multiplying a sequence by a float", node)
return isinstance(node.op, ast.Mult)
def fold_add(self, node, ty):
if not isinstance(node.left, ty):
return False
if not isinstance(node.right, ty):
return False
return isinstance(node.op, ast.Add)
def visit_BinOp(self, node):
if node in self.constant_expressions:
return node
node = self.generic_visit(node)
if self.fold_mult_left(node):
self.update = True
node.left.elts = [deepcopy(elt)
for _ in range(node.right.value)
for elt in node.left.elts]
return node.left
if self.fold_mult_right(node):
self.update = True
node.left, node.right = node.right, node.left
return self.visit(node)
for ty in (ast.List, ast.Tuple):
if self.fold_add(node, ty):
self.update = True
node.left.elts += node.right.elts
return node.left
return node
def visit_Subscript(self, node):
"""
>>> import gast as ast
>>> from pythran import passmanager, backend
>>> pm = passmanager.PassManager("test")
>>> node = ast.parse("def foo(a): a[1:][3]")
>>> _, node = pm.apply(PartialConstantFolding, node)
>>> _, node = pm.apply(ConstantFolding, node)
>>> print(pm.dump(backend.Python, node))
def foo(a):
a[4]
>>> node = ast.parse("def foo(a): a[::2][3]")
>>> _, node = pm.apply(PartialConstantFolding, node)
>>> _, node = pm.apply(ConstantFolding, node)
>>> print(pm.dump(backend.Python, node))
def foo(a):
a[6]
>>> node = ast.parse("def foo(a): a[-4:][5]")
>>> _, node = pm.apply(PartialConstantFolding, node)
>>> _, node = pm.apply(ConstantFolding, node)
>>> print(pm.dump(backend.Python, node))
def foo(a):
a[1]
"""
self.generic_visit(node)
if not isinstance(node.value, ast.Subscript):
return node
if not isinstance(node.value.slice, ast.Slice):
return node
if not isintegral(node.slice):
return node
slice_ = node.value.slice
index = node.slice
node = node.value
lower = slice_.lower or ast.Constant(0, None)
step = slice_.step or ast.Constant(1, None)
node.slice = ast.BinOp(lower,
ast.Add(),
ast.BinOp(index,
ast.Mult(),
step))
self.update = True
return node