%PDF- %PDF-
| Direktori : /lib/python3/dist-packages/pythran/ |
| Current File : //lib/python3/dist-packages/pythran/openmp.py |
'''
This modules contains OpenMP-related stuff.
* OMPDirective is used to represent OpenMP annotations in the AST
* GatherOMPData turns OpenMP-like string annotations into metadata
'''
from pythran.passmanager import Transformation
import pythran.metadata as metadata
from pythran.types.conversion import PYTYPE_TO_CTYPE_TABLE
from pythran.utils import isstr
from gast import AST
import gast as ast
import re
typenames = {t.__name__: t for t in PYTYPE_TO_CTYPE_TABLE}
keywords = {
'atomic',
'barrier',
'capture',
'cancel',
'collapse',
'copyin',
'copyprivate',
'critical',
'declare',
'default',
'final',
'firstprivate',
'flush',
'for',
'if',
'initializer',
'lastprivate',
'master',
'mergeable',
'none',
'nowait',
'num_threads',
'omp',
'ordered',
'parallel',
'private',
'read',
'reduction',
'schedule',
'section',
'sections',
'shared',
'simd',
'single',
'task',
'taskwait',
'taskyield',
'threadprivate',
'untied',
'update',
'write'
}
declare_keywords = {
'omp_in',
'omp_init',
'omp_orig',
'omp_out',
'omp_priv',
}
reserved_contex = {
'critical',
'declare',
'default',
'schedule',
'reduction',
}
class OMPDirective(AST):
'''Turn a string into a context-dependent metadata.
>>> o = OMPDirective("omp for private(a,b) shared(c)")
>>> o.s
'omp for private({},{}) shared({})'
>>> [ type(dep) for dep in o.deps ]
[<class 'gast.gast.Name'>, <class 'gast.gast.Name'>, \
<class 'gast.gast.Name'>]
>>> [ dep.id for dep in o.deps ]
['a', 'b', 'c']
'''
def __init__(self, *args): # no positional argument to be deep copyable
super(OMPDirective, self).__init__()
if not args:
return
self.deps = []
self.private_deps = []
self.shared_deps = []
def tokenize(s):
'''A simple contextual "parser" for an OpenMP string'''
# not completely satisfying if there are strings in if expressions
out = ''
par_count = 0
curr_index = 0
in_reserved_context = False
in_declare = False
in_shared = in_private = False
while curr_index < len(s):
m = re.match(r'^([a-zA-Z_]\w*)', s[curr_index:])
if m:
word = m.group(0)
curr_index += len(word)
if word in typenames:
out += PYTYPE_TO_CTYPE_TABLE[typenames[word]]
elif(in_reserved_context or
(in_declare and word in declare_keywords) or
(par_count == 0 and word in keywords)):
out += word
in_reserved_context = word in reserved_contex
in_declare |= word == 'declare'
in_private |= word == 'private'
in_shared |= word == 'shared'
else:
v = '{}'
self.deps.append(ast.Name(word, ast.Load(),
None, None))
if in_private:
self.private_deps.append(self.deps[-1])
if in_shared:
self.shared_deps.append(self.deps[-1])
out += v
elif s[curr_index] == '(':
par_count += 1
curr_index += 1
out += '('
elif s[curr_index] == ')':
par_count -= 1
curr_index += 1
out += ')'
if par_count == 0:
in_reserved_context = False
in_shared = in_private = False
else:
if s[curr_index] in ',:':
in_reserved_context = False
out += s[curr_index]
curr_index += 1
return out
self.s = tokenize(args[0])
self._fields = ('deps', 'shared_deps', 'private_deps')
##
class GatherOMPData(Transformation):
'''Walks node and collect string comments looking for OpenMP directives.'''
# there is a special handling for If and Expr, so not listed here
statements = ("FunctionDef", "Return", "Delete", "Assign", "AugAssign",
"Print", "For", "While", "Raise", "TryExcept", "TryFinally",
"Assert", "Import", "ImportFrom", "Pass", "Break",)
# these fields hold statement lists
statement_lists = ("body", "orelse", "finalbody",)
def __init__(self):
Transformation.__init__(self)
# Remap self.visit_XXXX() to self.attach_data() generic method
for s in GatherOMPData.statements:
setattr(self, "visit_" + s, self.attach_data)
self.current = list()
def isompdirective(self, node):
return isstr(node) and node.value.startswith("omp ")
def visit_Expr(self, node):
if self.isompdirective(node.value):
self.current.append(node.value.value)
return None
else:
self.attach_data(node)
return node
def visit_If(self, node):
if self.isompdirective(node.test):
self.visit(ast.Expr(node.test))
return self.visit(ast.If(ast.Constant(1, None),
node.body, node.orelse))
else:
return self.attach_data(node)
def attach_data(self, node):
'''Generic method called for visit_XXXX() with XXXX in
GatherOMPData.statements list
'''
if self.current:
for curr in self.current:
md = OMPDirective(curr)
metadata.add(node, md)
self.current = list()
# add a Pass to hold some directives
for field_name, field in ast.iter_fields(node):
if field_name in GatherOMPData.statement_lists:
if(field and
isinstance(field[-1], ast.Expr) and
self.isompdirective(field[-1].value)):
field.append(ast.Pass())
self.generic_visit(node)
# add an If to hold scoping OpenMP directives
directives = metadata.get(node, OMPDirective)
field_names = {n for n, _ in ast.iter_fields(node)}
has_no_scope = field_names.isdisjoint(GatherOMPData.statement_lists)
if directives and has_no_scope:
# some directives create a scope, but the holding stmt may not
# artificially create one here if needed
sdirective = ''.join(d.s for d in directives)
scoping = ('parallel', 'task', 'section')
if any(s in sdirective for s in scoping):
metadata.clear(node, OMPDirective)
node = ast.If(ast.Constant(1, None), [node], [])
for directive in directives:
metadata.add(node, directive)
return node