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) if not ASTMatcher(node).match(new_node): self.update = True return new_node except DamnTooLongPattern as e: print("W: ", e, " Assume no update happened.") except ConversionError as e: print('error in constant folding: ', e) raise except ToNotEval: pass except AttributeError as e: # this may miss a few optimization logger.info('During constant folding, bailing out due to: ' + e.args[0]) 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]) except Exception as e: raise PythranSyntaxError(str(e), node) return Transformation.generic_visit(self, node)
def visit_For(self, node): # if the user added some OpenMP directive, trust him and no unroll if metadata.get(node, OMPDirective): return node # don't visit children because of collapse # first unroll children if needed or possible self.generic_visit(node) # a break or continue in the loop prevents unrolling too has_break = any(self.gather(HasBreak, n) for n in node.body) has_cont = any(self.gather(HasContinue, n) for n in node.body) if has_break or has_cont: return node # do not unroll too much to prevent code growth node_count = self.gather(NodeCount, node) def unroll(elt, body): return [ast.Assign([deepcopy(node.target)], elt)] + body def dc(body, i, n): if i == n - 1: return body else: return deepcopy(body) def getrange(n): return getattr(getattr(n, 'func', None), 'attr', None) if isinstance(node.iter, (ast.Tuple, ast.List)): elts_count = len(node.iter.elts) total_count = node_count * elts_count issmall = total_count < LoopFullUnrolling.MAX_NODE_COUNT if issmall: self.update = True return sum([unroll(elt, dc(node.body, i, elts_count)) for i, elt in enumerate(node.iter.elts)], []) code = compile(ast.gast_to_ast(ast.Expression(node.iter)), '<loop unrolling>', 'eval') try: values = list(eval(code, {'builtins': __import__('builtins')})) except Exception: return node values_count = len(values) total_count = node_count * values_count issmall = total_count < LoopFullUnrolling.MAX_NODE_COUNT if issmall: try: new_node = sum([unroll(to_ast(elt), dc(node.body, i, values_count)) for i, elt in enumerate(values)], []) self.update = True return new_node except Exception: return node return node
def visit_For(self, node): # first unroll children if needed or possible self.generic_visit(node) # if the user added some OpenMP directive, trust him and no unroll has_omp = metadata.get(node, OMPDirective) # a break or continue in the loop prevents unrolling too has_break = any( self.passmanager.gather(HasBreak, n, self.ctx) for n in node.body) has_cont = any( self.passmanager.gather(HasContinue, n, self.ctx) for n in node.body) if has_omp or has_break or has_cont: return node # do not unroll too much to prevent code growth node_count = self.passmanager.gather(NodeCount, node, self.ctx) def unroll(elt): return ([ast.Assign([deepcopy(node.target)], elt)] + deepcopy(node.body)) def getrange(n): return getattr(getattr(n, 'func', None), 'attr', None) if isinstance(node.iter, (ast.Tuple, ast.List)): total_count = node_count * len(node.iter.elts) issmall = total_count < LoopFullUnrolling.MAX_NODE_COUNT if issmall: self.update = True return sum([unroll(elt) for elt in node.iter.elts], []) code = compile(ast.gast_to_ast(ast.Expression(node.iter)), '<loop unrolling>', 'eval') try: values = list( eval(code, {'__builtin__': __import__('__builtin__')})) except Exception as e: return node total_count = node_count * len(values) issmall = total_count < LoopFullUnrolling.MAX_NODE_COUNT if issmall: try: new_node = sum([unroll(to_ast(elt)) for elt in values], []) self.update = True return new_node except Exception as e: return node return node
def visit_Compare(self, node): """ Boolean are possible index. >>> import gast as ast >>> from pythran import passmanager, backend >>> node = ast.parse(''' ... def foo(): ... a = 2 or 3 ... b = 4 or 5 ... c = a < b ... d = b < 3 ... e = b == 4''') >>> pm = passmanager.PassManager("test") >>> res = pm.gather(RangeValues, node) >>> res['c'] Interval(low=1, high=1) >>> res['d'] Interval(low=0, high=0) >>> res['e'] Interval(low=0, high=1) """ if any( isinstance(op, (ast.In, ast.NotIn, ast.Is, ast.IsNot)) for op in node.ops): self.generic_visit(node) return self.add(node, Interval(0, 1)) curr = self.visit(node.left) res = [] for op, comparator in zip(node.ops, node.comparators): comparator = self.visit(comparator) fake = ast.Compare(ast.Name('x', ast.Load(), None), [op], [ast.Name('y', ast.Load(), None)]) fake = ast.Expression(fake) ast.fix_missing_locations(fake) expr = compile(ast.gast_to_ast(fake), '<range_values>', 'eval') res.append(eval(expr, {'x': curr, 'y': comparator})) if all(res): return self.add(node, Interval(1, 1)) elif any(r.low == r.high == 0 for r in res): return self.add(node, Interval(0, 0)) else: return self.add(node, Interval(0, 1))
def generic_visit(self, node): if node in self.constant_expressions: try: fake_node = ast.Expression( node.value if isinstance(node, ast.Index) else node) code = compile(ast.gast_to_ast(fake_node), '<constant folding>', 'eval') value = eval(code, self.env) new_node = to_ast(value) if(isinstance(node, ast.Index) and not isinstance(new_node, ast.Index)): new_node = ast.Index(new_node) 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: # FIXME union_ function is not handle by constant folding if "union_" in e.args[0]: return Transformation.generic_visit(self, node) elif "pythran" in e.args[0]: # FIXME: Can be fix giving a Python implementation for # these functions. return Transformation.generic_visit(self, node) raise 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) raise except Exception as e: raise PythranSyntaxError(str(e), node) else: return Transformation.generic_visit(self, node)