%PDF- %PDF-
| Direktori : /lib/python3/dist-packages/pythran/optimizations/ |
| Current File : //lib/python3/dist-packages/pythran/optimizations/modindex.py |
''' Simplify modulo computation based on index'''
from pythran.analyses import UseDefChains, Ancestors, Aliases, RangeValues
from pythran.analyses import Identifiers
from pythran.passmanager import Transformation
from pythran.tables import MODULES
import gast as ast
from copy import deepcopy
class ModIndex(Transformation):
'''
Simplify modulo on loop index
>>> import gast as ast
>>> from pythran import passmanager, backend
>>> pm = passmanager.PassManager("test")
>>> code = """
... def foo(x):
... y = builtins.len(x)
... for i in builtins.range(8):
... z = i % y"""
>>> node = ast.parse(code)
>>> _, node = pm.apply(ModIndex, node)
>>> print(pm.dump(backend.Python, node))
def foo(x):
y = builtins.len(x)
i_m = ((0 - 1) % y)
for i in builtins.range(8):
i_m = (0 if ((i_m + 1) == y) else (i_m + 1))
z = i_m
'''
def __init__(self):
Transformation.__init__(self, UseDefChains, Ancestors, Aliases,
RangeValues, Identifiers)
self.loops_mod = dict()
def single_def(self, node):
chain = self.use_def_chains[node]
return len(chain) == 1 and chain[0].node
def visit_BinOp(self, node):
if not isinstance(node.op, ast.Mod):
return self.generic_visit(node)
# check that right is a name defined once outside of loop
# TODO: handle expression instead of names
if not isinstance(node.right, ast.Name):
return self.generic_visit(node)
right_def = self.single_def(node.right)
if not right_def:
return self.generic_visit(node)
if self.range_values[node.right.id].low < 0:
return self.generic_visit(node)
# same for lhs
if not isinstance(node.left, ast.Name):
return self.generic_visit(node)
head = self.single_def(node.left)
if not head:
return self.generic_visit(node)
# check lhs is the actual index of a loop
loop = self.ancestors[head][-1]
if not isinstance(loop, ast.For):
return self.generic_visit(node)
if not isinstance(loop.iter, ast.Call):
return self.generic_visit(node)
# make sure rhs is defined out of the loop
if loop in self.ancestors[right_def]:
return self.generic_visit(node)
# gather range informations
range_ = None
for alias in self.aliases[loop.iter.func]:
if alias is MODULES['builtins']['range']:
range_ = alias
else:
break
if range_ is None:
return self.generic_visit(node)
# everything is setup for the transformation!
new_id = node.left.id + '_m'
i = 0
while new_id in self.identifiers:
new_id = '{}_m{}'.format(node.left.id, i)
i += 1
rargs = range_.args.args
lower = rargs[0] if len(rargs) > 1 else ast.Constant(0, None)
header = ast.Assign([ast.Name(new_id, ast.Store(), None, None)],
ast.BinOp(
ast.BinOp(deepcopy(lower),
ast.Sub(),
ast.Constant(1, None)),
ast.Mod(),
deepcopy(node.right)),
None)
incr = ast.BinOp(ast.Name(new_id, ast.Load(), None, None),
ast.Add(),
ast.Constant(1, None))
step = ast.Assign([ast.Name(new_id, ast.Store(), None, None)],
ast.IfExp(
ast.Compare(incr,
[ast.Eq()], [deepcopy(node.right)]),
ast.Constant(0, None),
deepcopy(incr)),
None)
self.loops_mod.setdefault(loop, []).append((header, step))
self.update = True
return ast.Name(new_id, ast.Load(), None, None)
def visit_For(self, node):
self.generic_visit(node)
if node not in self.loops_mod:
return node
headers = [h for h, _ in self.loops_mod[node]]
steps = [s for _, s in self.loops_mod[node]]
node.body = steps + node.body
return headers + [node]