Skip to content

Commit

Permalink
Merge pull request #555 from stfc/190_globals_to_arguments_transforma…
Browse files Browse the repository at this point in the history
…tion

#190 GlobalsToArguments transformation
  • Loading branch information
arporter authored Jan 8, 2020
2 parents e485d15 + 405e30b commit 6925972
Show file tree
Hide file tree
Showing 16 changed files with 917 additions and 42 deletions.
3 changes: 3 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
10) #614 for #612. Use the datatype of the Literal node in the SIR
backend rather than assuming all literals are real.

11) #555 for #190. Adds a GlobalsToArguments transformation that
converts kernel accesses to global data into arguments.

release 1.8.1 29th November 2019

1) #579 for #509. Improves the structuring of the html version
Expand Down
23 changes: 18 additions & 5 deletions doc/user_guide/transformations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ The generic transformations currently available are listed in
alphabetical order below (a number of these have specialisations which
can be found in the API-specific sections).

.. note:: PSyclone currently only supports OpenCL transformations for
the GOcean 1.0 API, the OpenACC Data transformation is
limited to the NEMO and GOcean 1.0 APIs and the OpenACC
Kernels transformation is limited to the NEMO and Dynamo0.3
APIs.
.. note:: PSyclone currently only supports OpenCL and KernelGlobalsToArguments
transformations for the GOcean 1.0 API, the OpenACC Data
transformation is limited to the NEMO and GOcean 1.0 APIs and the
OpenACC Kernels transformation is limited to the NEMO and Dynamo0.3
APIs.

.. note:: The directory layout of PSyclone is currently being restructured.
As a result of this some transformations are already in the new
Expand Down Expand Up @@ -182,6 +182,18 @@ can be found in the API-specific sections).

####

.. autoclass:: psyclone.transformations.KernelGlobalsToArguments
:members: apply
:noindex:

.. note:: This transformation modifies the PSyIR of both: the Invoke
Schedule where the transformed CodedKernel is located and its
associated Kernel Schedule.

.. note:: This transformation is only supported by the GOcean 1.0 API.

####

.. autoclass:: psyclone.transformations.KernelModuleInlineTrans
:members: apply
:noindex:
Expand Down Expand Up @@ -244,6 +256,7 @@ can be found in the API-specific sections).
:members: apply
:noindex:


Kernels
-------

Expand Down
Binary file modified psyclone.pdf
Binary file not shown.
27 changes: 25 additions & 2 deletions src/psyclone/gocean1p0.py
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,26 @@ def scalars(self):
args = args_filter(self._args, arg_types=["scalar"])
return [arg.name for arg in args]

def append(self, name):
''' Create and append a GOKernelArgument to the Argument list.
:param str name: name of the appended argument.
:raises TypeError: if the given name is not a string.
'''
from psyclone.parse.algorithm import Arg

if not isinstance(name, str):
raise TypeError(
"The name parameter given to GOKernelArguments.append method "
"should be a string, but found '{0}' instead.".
format(type(name).__name__))

descriptor = Descriptor(None, "") # Create a dummy descriptor
arg = Arg("variable", name, name)
argument = GOKernelArgument(descriptor, arg, self._parent_call)
self.args.append(argument)


class GOKernelArgument(KernelArgument):
''' Provides information about individual GOcean kernel call arguments
Expand All @@ -1480,8 +1500,11 @@ def __init__(self, arg, arg_info, call):
@property
def type(self):
''' Return the type of this kernel argument - whether it is a field,
a scalar or a grid_property (to be supplied by the PSy layer) '''
return self._arg.type
a scalar or a grid_property (to be supplied by the PSy layer).
If it has no type it defaults to scalar.'''
if hasattr(self._arg, 'type'):
return self._arg.type
return "scalar"

@property
def function_space(self):
Expand Down
56 changes: 56 additions & 0 deletions src/psyclone/psyGen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1833,9 +1833,21 @@ def __init__(self, KernFactory, BuiltInFactory, alg_calls=None):
self._opencl = False # Whether or not to generate OpenCL
# InvokeSchedule opencl_options default values
self._opencl_options = {"end_barrier": True}
# TODO: #312 Currently NameSpaceManager and SymbolTable coexist, but
# it would be better to merge them. The SymbolTable is only used
# for global variables extracted from the Kernels.
self._name_space_manager = NameSpaceFactory().create()
self._symbol_table = SymbolTable()
self._text_name = "InvokeSchedule"

@property
def symbol_table(self):
'''
:returns: Table containing symbol information for the schedule.
:rtype: :py:class:`psyclone.psyir.symbols.SymbolTable`
'''
return self._symbol_table

def set_opencl_options(self, options):
'''
Validate and store a set of options associated with the InvokeSchedule
Expand Down Expand Up @@ -1902,6 +1914,41 @@ def gen_code(self, parent):
from psyclone.f2pygen import UseGen, DeclGen, AssignGen, CommentGen, \
IfThenGen, CallGen

# Global symbols promoted from Kernel Globals are in the SymbolTable
# instead of the name_space_manager.
# First aggregate all globals variables from the same module in a map
module_map = {}
for globalvar in self.symbol_table.global_datasymbols:
module_name = globalvar.interface.container_symbol.name
if module_name in module_map:
module_map[module_name].append(globalvar.name)
else:
module_map[module_name] = [globalvar.name]

# Then we can produce the UseGen statements without repeating modules
for module_name, var_list in module_map.items():
parent.add(UseGen(parent, name=module_name, only=True,
funcnames=var_list))

# Add the SymbolTable symbols used into the NameSpaceManager to prevent
# clashes. Note that the global variables names are going to be used as
# arguments and are declared with 'AlgArgs' context.
# TODO: After #312 this will not be necessary.
for module_name, var_list in module_map.items():
self._name_space_manager.add_reserved_name(module_name)
for var_name in var_list:
newname = self._name_space_manager.create_name(
root_name=var_name,
context="AlgArgs",
label=var_name)
# There is a name clash with this variable name and we can not
# accept indexed names for global variables.
# TODO: #642 Improve global variables name clashes handling.
if var_name != newname:
raise KeyError(
"The imported variable '{0}' is already defined in the"
" NameSpaceManager of the Invoke.".format(var_name))

if self._opencl:
parent.add(UseGen(parent, name="iso_c_binding"))
parent.add(UseGen(parent, name="clfortran"))
Expand Down Expand Up @@ -4672,6 +4719,15 @@ def scalars(self):
raise NotImplementedError(
"Arguments.scalars must be implemented in sub-class")

def append(self, name):
''' Abstract method to append KernelArguments to the Argument
list.
:param str name: name of the appended argument.
'''
raise NotImplementedError(
"Arguments.append must be implemented in sub-class")


class DataAccess(object):
'''A helper class to simplify the determination of dependencies due to
Expand Down
24 changes: 12 additions & 12 deletions src/psyclone/psyir/symbols/datasymbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,28 +358,28 @@ def constant_value(self, new_value):
'''
if new_value is not None:
if not self.is_local:
if self.is_argument:
raise ValueError(
"Error setting '{0}' constant value. A DataSymbol with a "
"constant value is currently limited to Local interfaces "
"but found '{1}'.".format(self.name, self.interface))
"Error setting constant value for symbol '{0}'. A "
"DataSymbol with an ArgumentInterface can not have a "
"constant value.".format(self.name))
if self.is_array:
raise ValueError(
"Error setting '{0}' constant value. A DataSymbol with a "
"constant value must be a scalar but a shape was found."
"".format(self.name))
"Error setting constant value for symbol '{0}'. A "
"DataSymbol with a constant value must be a scalar but "
"a shape was found.".format(self.name))
try:
lookup = TYPE_MAP_TO_PYTHON[self.datatype]
except KeyError:
raise ValueError(
"Error setting '{0}' constant value. Constant values are "
"not supported for '{1}' datatypes."
"Error setting constant value for symbol '{0}'. "
"Constant values are not supported for '{1}' datatypes."
"".format(self.name, self.datatype))
if not isinstance(new_value, lookup):
raise ValueError(
"Error setting '{0}' constant value. This DataSymbol "
"instance datatype is '{1}' which means the constant "
"value is expected to be '{2}' but found '{3}'."
"Error setting constant value for symbol '{0}'. This "
"DataSymbol instance datatype is '{1}' which means the "
"constant value is expected to be '{2}' but found '{3}'."
"".format(self.name, self.datatype,
TYPE_MAP_TO_PYTHON[self.datatype],
type(new_value)))
Expand Down
50 changes: 49 additions & 1 deletion src/psyclone/psyir/symbols/symboltable.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@

from __future__ import print_function
from collections import OrderedDict
from psyclone.psyir.symbols import Symbol, DataSymbol
from psyclone.psyir.symbols import Symbol, DataSymbol, GlobalInterface, \
ContainerSymbol


class SymbolTable(object):
Expand Down Expand Up @@ -337,6 +338,53 @@ def data_arguments(self):
"Abstract property. Which symbols are data arguments is"
" API-specific.")

def copy_external_global(self, globalvar):
'''
Copy the given global variable (and its referenced ContainerSymbol if
needed) into the SymbolTable.
:param globalvar: the variable to be copied in.
:type globalvar: :py:class:`psyclone.psyir.symbols.DataSymbol`
:raises TypeError: if the given variable is not a global variable.
:raises KeyError: if the given variable name already exists in the \
symbol table.
'''
if not isinstance(globalvar, DataSymbol):
raise TypeError(
"The globalvar argument of SymbolTable.copy_external_global"
" method should be a DataSymbol, but found '{0}'."
"".format(type(globalvar).__name__))

if not globalvar.is_global:
raise TypeError(
"The globalvar argument of SymbolTable.copy_external_"
"global method should have a GlobalInterface interface, "
"but found '{0}'.".format(type(globalvar.interface).__name__))

external_container_name = globalvar.interface.container_symbol.name

# If the Container is not yet in the SymbolTable we need to
# create one and add it.
if external_container_name not in self:
self.add(ContainerSymbol(external_container_name))

# Copy the variable into the SymbolTable with the appropriate interface
if globalvar.name not in self:
new_symbol = globalvar.copy()
container_ref = self.lookup(external_container_name)
new_symbol.interface = GlobalInterface(container_ref)
self.add(new_symbol)
else:
# If it already exists it must refer to the same Container
if not (self.lookup(globalvar.name).is_global and
self.lookup(globalvar.name).interface
.container_symbol.name == external_container_name):
raise KeyError(
"Couldn't copy '{0}' into the SymbolTable. The"
" name '{1}' is already used by another symbol."
"".format(globalvar, globalvar.name))

def view(self):
'''
Print a representation of this Symbol Table to stdout.
Expand Down
Loading

0 comments on commit 6925972

Please sign in to comment.