%PDF- %PDF-
| Direktori : /lib/python3/dist-packages/pythran/analyses/ |
| Current File : //lib/python3/dist-packages/pythran/analyses/argument_read_once.py |
""" ArgumentReadOnce counts the usages of each argument of each function. """
from pythran.analyses.aliases import Aliases
from pythran.analyses.global_declarations import GlobalDeclarations
from pythran.passmanager import ModuleAnalysis
from pythran.tables import MODULES
import pythran.intrinsic as intrinsic
import gast as ast
from functools import reduce
class ArgumentReadOnce(ModuleAnalysis):
"""
Counts the usages of each argument of each function.
Attributes
----------
result : {FunctionEffects}
Number of use for each argument of each function.
node_to_functioneffect : {???: ???}
FunctionDef ast node to function effect binding.
"""
class FunctionEffects(object):
def __init__(self, node):
self.func = node
self.dependencies = lambda ctx: 0
if isinstance(node, ast.FunctionDef):
self.read_effects = [-1] * len(node.args.args)
elif isinstance(node, intrinsic.Intrinsic):
self.read_effects = [
1 if isinstance(x, intrinsic.ReadOnceEffect)
else 2 for x in node.argument_effects]
elif isinstance(node, ast.alias):
self.read_effects = []
else:
raise NotImplementedError
class ConstructorEffects(object):
def __init__(self, node):
self.func = node
self.dependencies = lambda ctx: 0
self.read_effects = [0]
class Context(object):
def __init__(self, function, index, path, global_dependencies):
self.function = function
self.index = index
self.path = path
self.global_dependencies = global_dependencies
def __init__(self):
""" Basic initialiser for class attributes. """
self.result = set()
self.node_to_functioneffect = dict()
super(ArgumentReadOnce, self).__init__(Aliases, GlobalDeclarations)
def prepare(self, node):
"""
Initialise arguments effects as this analysis in inter-procedural.
Initialisation done for Pythonic functions and default values set for
user defined functions.
"""
super(ArgumentReadOnce, self).prepare(node)
# global functions init
for n in self.global_declarations.values():
fe = ArgumentReadOnce.FunctionEffects(n)
self.node_to_functioneffect[n] = fe
self.result.add(fe)
# Pythonic functions init
def save_effect(module):
""" Recursively save read once effect for Pythonic functions. """
for intr in module.values():
if isinstance(intr, dict): # Submodule case
save_effect(intr)
else:
fe = ArgumentReadOnce.FunctionEffects(intr)
self.node_to_functioneffect[intr] = fe
self.result.add(fe)
if isinstance(intr, intrinsic.Class): # Class case
save_effect(intr.fields)
for module in MODULES.values():
save_effect(module)
def run(self, node):
result = super(ArgumentReadOnce, self).run(node)
for fun in result:
for i in range(len(fun.read_effects)):
self.recursive_weight(fun, i, set())
self.result = {f.func: f.read_effects for f in result}
return self.result
def recursive_weight(self, function, index, predecessors):
# TODO : Find out why it happens in some cases
if len(function.read_effects) <= index:
return 0
if function.read_effects[index] == -1:
# In case of recursive/cyclic calls
cycle = function in predecessors
predecessors.add(function)
if cycle:
function.read_effects[index] = 2 * function.dependencies(
ArgumentReadOnce.Context(function, index,
predecessors, False))
else:
function.read_effects[index] = function.dependencies(
ArgumentReadOnce.Context(function, index,
predecessors, True))
return function.read_effects[index]
def argument_index(self, node):
while isinstance(node, ast.Subscript):
node = node.value
if node in self.aliases:
for n_alias in self.aliases[node]:
try:
return self.current_function.func.args.args.index(n_alias)
except ValueError:
pass
return -1
def local_effect(self, node, effect):
index = self.argument_index(node)
return lambda ctx: effect if index == ctx.index else 0
def generic_visit(self, node):
lambdas = [self.visit(child) for child in ast.iter_child_nodes(node)]
return lambda ctx: sum(l(ctx) for l in lambdas)
def visit_FunctionDef(self, node):
self.current_function = self.node_to_functioneffect[node]
assert self.current_function in self.result
self.current_function.dependencies = self.generic_visit(node)
def visit_Return(self, node):
dep = self.generic_visit(node)
if isinstance(node.value, ast.Name):
local = self.local_effect(node.value, 2)
return lambda ctx: dep(ctx) + local(ctx)
else:
return dep
def visit_Assign(self, node):
dep = self.generic_visit(node)
local = [self.local_effect(t, 2) for t in node.targets
if isinstance(t, ast.Subscript)]
return lambda ctx: dep(ctx) + sum(l(ctx) for l in local)
def visit_AugAssign(self, node):
dep = self.generic_visit(node)
local = self.local_effect(node.target, 2)
return lambda ctx: dep(ctx) + local(ctx)
def visit_For(self, node):
iter_local = self.local_effect(node.iter, 1)
iter_deps = self.visit(node.iter)
body_deps = [self.visit(stmt) for stmt in node.body]
else_deps = [self.visit(stmt) for stmt in node.orelse]
return lambda ctx: iter_local(ctx) + iter_deps(ctx) + 2 * sum(
l(ctx) for l in body_deps) + sum(l(ctx) for l in else_deps)
def visit_While(self, node):
test_deps = self.visit(node.test)
body_deps = [self.visit(stmt) for stmt in node.body]
else_deps = [self.visit(stmt) for stmt in node.orelse]
return lambda ctx: test_deps(ctx) + 2 * sum(
l(ctx) for l in body_deps) + sum(l(ctx) for l in else_deps)
def visit_If(self, node):
test_deps = self.visit(node.test)
body_deps = [self.visit(stmt) for stmt in node.body]
else_deps = [self.visit(stmt) for stmt in node.orelse]
return lambda ctx: test_deps(ctx) + max(sum(
l(ctx) for l in body_deps), sum(l(ctx) for l in else_deps))
def visit_Call(self, node):
l0 = self.generic_visit(node)
index_corres = dict()
func = None
for i, arg in enumerate(node.args):
n = self.argument_index(arg)
if n >= 0:
func_aliases = self.aliases[node.func]
# 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
func = self.node_to_functioneffect[func_alias]
index_corres[n] = i
def merger(ctx):
base = l0(ctx)
if (ctx.index in index_corres) and ctx.global_dependencies:
rec = self.recursive_weight(func, index_corres[ctx.index],
ctx.path)
else:
rec = 0
return base + rec
return merger
def visit_Subscript(self, node):
dep = self.generic_visit(node)
local = self.local_effect(node.value, 2)
return lambda ctx: dep(ctx) + local(ctx)
def visit_comprehension(self, node):
dep = self.generic_visit(node)
local = self.local_effect(node.iter, 1)
return lambda ctx: dep(ctx) + local(ctx)