37 '''Contains the PSyData transformation.
44 OMPDoDirective, ACCDirective, ACCLoopDirective, Routine
47 import TransformationError
51 ''' Create a PSyData region around a list of statements. For
54 >>> from psyclone.parse.algorithm import parse
55 >>> from psyclone.parse.utils import ParseError
56 >>> from psyclone.psyGen import PSyFactory
58 >>> ast, invoke_info = parse(SOURCE_FILE, api=api)
59 >>> psy = PSyFactory(api).create(invoke_info)
61 >>> from psyclone.psyir.transformations import PSyDataTrans
62 >>> data_trans = PSyDataTrans()
64 >>> schedule = psy.invokes.get('invoke_0').schedule
65 >>> # Uncomment the following line to see a text view of the schedule
66 >>> # print(schedule.view())
68 >>> # Enclose all children within a single PSyData region
69 >>> data_trans.apply(schedule.children)
70 >>> # Uncomment the following line to see a text view of the schedule
71 >>> # print(schedule.view())
72 >>> # Or to use custom region name:
73 >>> data_trans.apply(schedule.children,
74 ... {"region_name": ("module","region")})
76 :param node_class: The Node class of which an instance will be inserted \
77 into the tree (defaults to PSyDataNode).
78 :type node_class: :py:class:`psyclone.psyir.nodes.ExtractNode`
83 excluded_node_types = (Return,)
89 _used_kernel_names = {}
91 def __init__(self, node_class=PSyDataNode):
97 return (f
"Create a sub-tree of the PSyIR that has a node of type "
98 f
"{self._node_class.__name__} at its root.")
103 '''This function returns the name of the transformation.
104 It uses the Python 2/3 compatible way of returning the
105 class name as a string, which means that the same function can
106 be used for all derived classes.
108 :returns: the name of this transformation as a string.
112 return self.__class__.__name__
116 '''Returns a new dictionary with additional options, specific to the
117 transformation, that will be added to the user option. Any values
118 specified by the user will take precedence.
120 :returns: a dictionary with additional options.
121 :rtype: Dict[str, Any]
128 '''This function returns a new dictionary which contains the default
129 options for this transformation plus al user-specified options.
130 Any user-specified option will take precedence over the default
133 :param options: a dictionary with options for transformations.
134 :type options: Dict[str, Any]
135 :returns: a new dictionary which merges the default options with \
136 the user-specified options.
137 :rtype: Dict[str:Any]
144 new_options.update(options)
149 '''This function returns the region and module name. If they are
150 specified in the user options, these names will just be returned (it
151 is then up to the user to guarantee uniqueness). Otherwise a name
152 based on the module and invoke will be created using indices to
153 make sure the name is unique.
155 :param nodes: a list of nodes.
156 :type nodes: list of :py:obj:`psyclone.psyir.nodes.Node`
157 :param options: a dictionary with options for transformations.
158 :type options: Dict[str, Any]
159 :param (str,str) options["region_name"]: an optional name to \
160 use for this PSyData area, provided as a 2-tuple containing a \
161 location name followed by a local name. The pair of strings \
162 should uniquely identify a region unless aggregate information \
163 is required (and is supported by the runtime library).
168 name = options.get(
"region_name",
None)
171 if not isinstance(name, tuple)
or not len(name) == 2
or \
172 not name[0]
or not isinstance(name[0], str)
or \
173 not name[1]
or not isinstance(name[1], str):
175 "Error in PSyDataTrans. The name must be a "
176 "tuple containing two non-empty strings.")
181 invoke = nodes[0].ancestor(InvokeSchedule).invoke
182 module_name = invoke.invokes.psy.name
185 region_name = invoke.name
188 kerns.extend(node.walk(Kern))
193 region_name += f
":{kerns[0].name}"
197 key = module_name +
"|" + region_name
198 idx = PSyDataTrans._used_kernel_names.get(key, 0)
199 PSyDataTrans._used_kernel_names[key] = idx + 1
200 region_name += f
":r{idx}"
201 return (module_name, region_name)
206 Checks that the supplied list of nodes is valid, that the location
207 for this node is valid (not between a loop-directive and its loop),
208 that there aren't any name clashes with symbols that must be
209 imported from the appropriate PSyData library and finally, calls the
210 validate method of the base class.
212 :param nodes: a node or list of nodes to be instrumented with \
214 :type nodes: (list of) :py:class:`psyclone.psyir.nodes.Loop`
216 :param options: a dictionary with options for transformations.
217 :type options: Optional[Dict[str, Any]]
218 :param str options["prefix"]: a prefix to use for the PSyData module \
219 name (``PREFIX_psy_data_mod``) and the PSyDataType \
220 (``PREFIX_PSYDATATYPE``) - a "_" will be added automatically. \
222 :param (str,str) options["region_name"]: an optional name to \
223 use for this PSyData area, provided as a 2-tuple containing a \
224 location name followed by a local name. The pair of strings \
225 should uniquely identify a region unless aggregate information \
226 is required (and is supported by the runtime library).
228 :raises TransformationError: if the supplied list of nodes is empty.
229 :raises TransformationError: if the PSyData node is inserted \
230 between an OpenMP/ACC directive and the loop(s) to which it \
232 :raises TransformationError: if the 'prefix' or 'region_name' options \
234 :raises TransformationError: if there will be a name clash between \
235 any existing symbols and those that must be imported from the \
236 appropriate PSyData library.
243 raise TransformationError(
"Cannot apply transformation to an "
244 "empty list of nodes.")
246 node_parent = node_list[0].parent
247 if isinstance(node_parent, Schedule)
and \
248 isinstance(node_parent.parent, (OMPDoDirective, ACCLoopDirective)):
249 raise TransformationError(
"A PSyData node cannot be inserted "
250 "between an OpenMP/ACC directive and "
251 "the loop(s) to which it applies!")
253 if node_list[0].ancestor(ACCDirective):
254 raise TransformationError(
"A PSyData node cannot be inserted "
255 "inside an OpenACC region.")
258 if "region_name" in my_options:
259 name = my_options[
"region_name"]
261 if not isinstance(name, tuple)
or not len(name) == 2
or \
262 not name[0]
or not isinstance(name[0], str)
or \
263 not name[1]
or not isinstance(name[1], str):
264 raise TransformationError(
265 f
"Error in {self.name}. User-supplied region name "
266 f
"must be a tuple containing two non-empty strings.")
268 prefix = my_options.get(
"prefix",
None)
269 if "prefix" in my_options:
270 prefix = my_options.get(
"prefix",
None)
271 if prefix
not in Config.get().valid_psy_data_prefixes:
272 raise TransformationError(
273 f
"Error in 'prefix' parameter: found '{prefix}', while"
274 f
" one of {Config.get().valid_psy_data_prefixes} was "
275 f
"expected as defined in {Config.get().filename}")
279 pdata_node = self.
_node_class_node_class(options=my_options)
280 table = node_list[0].scope.symbol_table
281 for name
in ([sym.name
for sym
in pdata_node.imported_symbols] +
282 [pdata_node.fortran_module]):
284 _ = table.lookup_with_tag(name)
285 except KeyError
as err:
290 _ = table.lookup(name)
291 raise TransformationError(
292 f
"Cannot add PSyData calls because there is already a "
293 f
"symbol named '{name}' which clashes with one of "
294 f
"those used by the PSyclone PSyData API. ")
from err
298 super().
validate(node_list, my_options)
301 def apply(self, nodes, options=None):
303 '''Apply this transformation to a subset of the nodes within a
304 schedule - i.e. enclose the specified Nodes in the
305 schedule within a single PSyData region.
307 :param nodes: can be a single node or a list of nodes.
308 :type nodes: :py:obj:`psyclone.psyir.nodes.Node` or list of \
309 :py:obj:`psyclone.psyir.nodes.Node`
310 :param options: a dictionary with options for transformations.
311 :type options: Optional[Dict[str, Any]]
312 :param str options["prefix"]: a prefix to use for the PSyData module \
313 name (``PREFIX_psy_data_mod``) and the PSyDataType \
314 (``PREFIX_PSYDATATYPE``) - a "_" will be added automatically. \
316 :param (str,str) options["region_name"]: an optional name to \
317 use for this PSyData area, provided as a 2-tuple containing a \
318 location name followed by a local name. The pair of strings \
319 should uniquely identify a region unless aggregate information \
320 is required (and is supported by the runtime library).
331 parent = node_list[0].parent
332 position = node_list[0].position
335 table = node_list[0].ancestor(Routine).symbol_table
344 for node
in node_list:
347 psy_data_node = self.
_node_class_node_class.create(
348 node_list, symbol_table=table, options=my_options)
349 parent.addchild(psy_data_node, position)
354 __all__ = [
'PSyDataTrans']