%PDF- %PDF-
Direktori : /lib/python3/dist-packages/pythran/optimizations/ |
Current File : //lib/python3/dist-packages/pythran/optimizations/inline_builtins.py |
""" Expand some builtins implementation when it is profitable.""" from pythran.analyses import Aliases from pythran.analyses.pure_expressions import PureExpressions from pythran.passmanager import Transformation from pythran.tables import MODULES from pythran.intrinsic import FunctionIntr from pythran.utils import path_to_attr, path_to_node from pythran.syntax import PythranSyntaxError from copy import deepcopy import gast as ast class InlineBuiltins(Transformation): """ Replace some builtins by their bodies. This may trigger some extra optimizations later on! >>> import gast as ast >>> from pythran import passmanager, backend >>> pm = passmanager.PassManager("test") >>> node = ast.parse(''' ... def foo(a): ... return a + 1 ... def bar(b): ... return builtins.map(bar, (1, 2))''') >>> _, node = pm.apply(InlineBuiltins, node) >>> print(pm.dump(backend.Python, node)) def foo(a): return (a + 1) def bar(b): return [bar(1), bar(2)] """ def __init__(self): Transformation.__init__(self, Aliases, PureExpressions) def inlineBuiltinsXMap(self, node): self.update = True elts = [] nelts = min(len(n.elts) for n in node.args[1:]) for i in range(nelts): elts.append([n.elts[i] for n in node.args[1:]]) return ast.List([ast.Call(node.args[0], elt, []) for elt in elts], ast.Load()) def inlineBuiltinsMap(self, node): if not isinstance(node, ast.Call): return node func_aliases = self.aliases[node.func] if len(func_aliases) != 1: return node obj = next(iter(func_aliases)) if obj is not MODULES['builtins']['map']: return node if not all(isinstance(arg, (ast.List, ast.Tuple)) for arg in node.args[1:]): return node mapped_func_aliases = self.aliases[node.args[0]] if len(mapped_func_aliases) != 1: return node obj = next(iter(mapped_func_aliases)) if not isinstance(obj, (ast.FunctionDef, FunctionIntr)): return node # all preconditions are met, do it! return self.inlineBuiltinsXMap(node) def visit_Call(self, node): node = self.generic_visit(node) node = self.inlineBuiltinsMap(node) return node def make_array_index(self, base, size, index): if isinstance(base, ast.Constant): return ast.Constant(base.value, None) if size == 1: return deepcopy(base.elts[0]) return base.elts[index] def fixedSizeArray(self, node): if isinstance(node, ast.Constant): return node, 1 if isinstance(node, (ast.List, ast.Tuple)): return node, len(node.elts) if not isinstance(node, ast.Call): return None, 0 func_aliases = self.aliases[node.func] if len(func_aliases) != 1: return None, 0 obj = next(iter(func_aliases)) if obj not in (MODULES['numpy']['array'], MODULES['numpy']['asarray']): return None, 0 if len(node.args) != 1: return None, 0 if isinstance(node.args[0], (ast.List, ast.Tuple)): return node.args[0], len(node.args[0].elts) return None, 0 def inlineFixedSizeArrayBinOp(self, node): alike = ast.List, ast.Tuple, ast.Constant if isinstance(node.left, alike) and isinstance(node.right, alike): return node lbase, lsize = self.fixedSizeArray(node.left) rbase, rsize = self.fixedSizeArray(node.right) if not lbase or not rbase: return node if rsize != 1 and lsize != 1 and rsize != lsize: raise PythranSyntaxError("Invalid numpy broadcasting", node) self.update = True operands = [ast.BinOp(self.make_array_index(lbase, lsize, i), type(node.op)(), self.make_array_index(rbase, rsize, i)) for i in range(max(lsize, rsize))] res = ast.Call(path_to_attr(('numpy', 'array')), [ast.Tuple(operands, ast.Load())], []) self.aliases[res.func] = {path_to_node(('numpy', 'array'))} return res def visit_BinOp(self, node): node = self.generic_visit(node) node = self.inlineFixedSizeArrayBinOp(node) return node def inlineFixedSizeArrayUnaryOp(self, node): if isinstance(node.operand, (ast.Constant, ast.List, ast.Tuple)): return node base, size = self.fixedSizeArray(node.operand) if not base: return node self.update = True operands = [ast.UnaryOp(type(node.op)(), self.make_array_index(base, size, i)) for i in range(size)] res = ast.Call(path_to_attr(('numpy', 'array')), [ast.Tuple(operands, ast.Load())], []) self.aliases[res.func] = {path_to_node(('numpy', 'array'))} return res def visit_UnaryOp(self, node): node = self.generic_visit(node) node = self.inlineFixedSizeArrayUnaryOp(node) return node def inlineFixedSizeArrayCompare(self, node): if len(node.comparators) != 1: return node node_right = node.comparators[0] alike = ast.Constant, ast.List, ast.Tuple if isinstance(node.left, alike) and isinstance(node_right, alike): return node lbase, lsize = self.fixedSizeArray(node.left) rbase, rsize = self.fixedSizeArray(node_right) if not lbase or not rbase: return node if rsize != 1 and lsize != 1 and rsize != lsize: raise PythranSyntaxError("Invalid numpy broadcasting", node) self.update = True operands = [ast.Compare(self.make_array_index(lbase, lsize, i), [type(node.ops[0])()], [self.make_array_index(rbase, rsize, i)]) for i in range(max(lsize, rsize))] res = ast.Call(path_to_attr(('numpy', 'array')), [ast.Tuple(operands, ast.Load())], []) self.aliases[res.func] = {path_to_node(('numpy', 'array'))} return res def visit_Compare(self, node): node = self.generic_visit(node) node = self.inlineFixedSizeArrayCompare(node) return node