%PDF- %PDF-
Direktori : /lib/python3/dist-packages/pythran/analyses/ |
Current File : //lib/python3/dist-packages/pythran/analyses/argument_effects.py |
""" ArgumentEffects computes write effect on arguments. """ from pythran.analyses.aliases import Aliases from pythran.analyses.global_declarations import GlobalDeclarations from pythran.passmanager import ModuleAnalysis from pythran.tables import MODULES from pythran.graph import DiGraph # FIXME: investigate why we need to import it that way from pythran import intrinsic import gast as ast from functools import reduce class FunctionEffects(object): def __init__(self, node): self.func = node if isinstance(node, ast.FunctionDef): self.update_effects = [False] * len(node.args.args) elif isinstance(node, intrinsic.Intrinsic): self.update_effects = [isinstance(x, intrinsic.UpdateEffect) for x in node.argument_effects] elif isinstance(node, ast.alias): self.update_effects = [] elif isinstance(node, intrinsic.Class): self.update_effects = [] else: raise NotImplementedError # Compute the intrinsic effects only once IntrinsicArgumentEffects = {} def save_function_effect(module): """ Recursively save function effect for pythonic functions. """ for intr in module.values(): if isinstance(intr, dict): # Submodule case save_function_effect(intr) else: fe = FunctionEffects(intr) IntrinsicArgumentEffects[intr] = fe if isinstance(intr, intrinsic.Class): save_function_effect(intr.fields) for module in MODULES.values(): save_function_effect(module) class ArgumentEffects(ModuleAnalysis): """Gathers inter-procedural effects on function arguments.""" def __init__(self): self.result = DiGraph() self.node_to_functioneffect = IntrinsicArgumentEffects.copy() for fe in IntrinsicArgumentEffects.values(): self.result.add_node(fe) super(ArgumentEffects, self).__init__(Aliases, GlobalDeclarations) def prepare(self, node): """ Initialise arguments effects as this analyse is inter-procedural. Initialisation done for Pythonic functions and default value set for user defined functions. """ super(ArgumentEffects, self).prepare(node) for n in self.global_declarations.values(): fe = FunctionEffects(n) self.node_to_functioneffect[n] = fe self.result.add_node(fe) def run(self, node): result = super(ArgumentEffects, self).run(node) candidates = set(result) while candidates: function = candidates.pop() for ue in enumerate(function.update_effects): update_effect_idx, update_effect = ue if not update_effect: continue for pred in result.predecessors(function): edge = result.edges[pred, function] for fp in enumerate(edge["formal_parameters"]): i, formal_parameter_idx = fp # propagate the impurity backward if needed. # Afterward we may need another graph iteration ith_effectiv = edge["effective_parameters"][i] if(formal_parameter_idx == update_effect_idx and not pred.update_effects[ith_effectiv]): pred.update_effects[ith_effectiv] = True candidates.add(pred) self.result = {f.func: f.update_effects for f in result} return self.result def argument_index(self, node): while isinstance(node, ast.Subscript): node = node.value for node_alias in self.aliases[node]: while isinstance(node_alias, ast.Subscript): node_alias = node_alias.value if node_alias in self.current_arguments: return self.current_arguments[node_alias] if node_alias in self.current_subscripted_arguments: return self.current_subscripted_arguments[node_alias] return -1 def visit_FunctionDef(self, node): self.current_function = self.node_to_functioneffect[node] self.current_arguments = {arg: i for i, arg in enumerate(node.args.args)} self.current_subscripted_arguments = dict() assert self.current_function in self.result self.generic_visit(node) def visit_For(self, node): ai = self.argument_index(node.iter) if ai >= 0: self.current_subscripted_arguments[node.target] = ai self.generic_visit(node) def visit_AugAssign(self, node): n = self.argument_index(node.target) if n >= 0: self.current_function.update_effects[n] = True self.generic_visit(node) def visit_Assign(self, node): for t in node.targets: if isinstance(t, ast.Subscript): n = self.argument_index(t) if n >= 0: self.current_function.update_effects[n] = True self.generic_visit(node) def visit_Call(self, node): for i, arg in enumerate(node.args): n = self.argument_index(arg) if n >= 0: func_aliases = self.aliases[node.func] # pessimistic case: no alias found if func_aliases is None: self.current_function.update_effects[n] = True continue # expand argument if any func_aliases = reduce( lambda x, y: x + ( # all functions list(self.node_to_functioneffect.keys()) if (isinstance(y, ast.Name) and self.argument_index(y) >= 0) else [y]), func_aliases, list()) for func_alias in func_aliases: # special hook for binded functions if isinstance(func_alias, ast.Call): bound_name = func_alias.args[0].id func_alias = self.global_declarations[bound_name] if func_alias is intrinsic.UnboundValue: continue if func_alias not in self.node_to_functioneffect: continue if func_alias is MODULES['functools']['partial']: base_func_aliases = self.aliases[node.args[0]] fe = self.node_to_functioneffect[func_alias] if len(base_func_aliases) == 1: base_func_alias = next(iter(base_func_aliases)) fe = self.node_to_functioneffect.get( base_func_alias, fe) else: fe = self.node_to_functioneffect[func_alias] predecessors = self.result.predecessors(fe) if self.current_function not in predecessors: self.result.add_edge( self.current_function, fe, effective_parameters=[], formal_parameters=[]) edge = self.result.edges[self.current_function, fe] edge["effective_parameters"].append(n) edge["formal_parameters"].append(i) self.generic_visit(node)