40 ''' This module contains the Loop node implementation.'''
54 '''Node representing a loop within the PSyIR. It has 4 mandatory children:
55 the first one represents the loop lower bound, the second one represents
56 the loop upper bound, the third one represents the step value and the
57 fourth one is always a PSyIR Schedule node containing the statements inside
60 (Note: Loop only represents the equivalent to Fortran counted do loops.
61 This means the loop is bounded by start/stop/step expressions evaluated
62 before the loop starts. See WhileLoop for while loops, including the
63 Fortran do while and do loop with no condition.)
65 :param variable: optional reference to the loop iterator \
66 variable. Defaults to None.
67 :type variable: Optional[:py:class:`psyclone.psyir.symbols.DataSymbol`]
68 :param annotations: One or more labels that provide additional information\
69 about the node (primarily relating to the input code that it was \
71 :type annotations: Optional[List[str]]
72 :param kwargs: additional keyword arguments provided to the PSyIR node.
73 :type kwargs: unwrapped dict.
75 :raises InternalError: if the 'was_single_stmt' annotation is supplied \
76 without the 'was_where' annotation.
79 valid_annotations = (
'was_where',
'was_single_stmt',
'chunked')
81 _children_valid_format =
"DataNode, DataNode, DataNode, Schedule"
87 _loop_type_inference_rules = {}
89 def __init__(self, variable=None, annotations=None, **kwargs):
93 if 'was_single_stmt' in annotations
and \
94 'was_where' not in annotations:
96 f
"A Loop with the 'was_single_stmt' annotation "
97 f
"must also have the 'was_where' annotation but"
98 f
" got: {annotations}")
100 super().__init__(self, annotations=annotations, **kwargs)
103 if variable
is not None:
108 Checks whether two nodes are equal. Two Loop nodes are equal
109 if they have the same iteration variable and their children are
112 :param object other: the object to check equality to.
114 :returns: whether other is equal to self.
117 is_eq = super().
__eq__(other)
128 :returns: the type of this loop.
136 Specify the rules that define a loop type by inspecting its variable,
137 name. This affects all instances of the Loop class. For example:
142 "lon": {"variable": "ji"},
143 "lat": {"variable": "jj"}
146 :param rules: new set of rules for inferring loop_types.
147 :type rules: dict[str, dict[str, str]]
154 if not isinstance(rules, dict):
155 raise TypeError(f
"The rules argument must be of type 'dict' but "
156 f
"found '{type(rules)}'.")
157 for key, rule
in rules.items():
158 if not isinstance(key, str):
159 raise TypeError(f
"The rules keys must be of type 'str' but "
160 f
"found '{type(key)}'.")
161 if not isinstance(rule, dict):
162 raise TypeError(f
"The rules values must be of type 'dict' but "
163 f
"found '{type(rule)}'.")
164 for rkey, value
in rule.items():
165 if not isinstance(rkey, str)
or not isinstance(value, str):
167 f
"All the values of the rule definition must be "
168 f
"of type 'str' but found '{rule}'.")
169 if rkey !=
"variable":
170 raise TypeError(f
"Currently only the 'variable' rule key"
171 f
" is accepted, but found: '{rkey}'.")
172 if "variable" not in rule:
173 raise TypeError(f
"A rule must at least have a 'variable' field"
174 f
" to specify the loop variable name that "
175 f
"defines this loop_type, but the rule for "
176 f
"'{key}' does not have it.")
180 for key, rule
in rules.items():
181 inference_rules[rule[
"variable"]] = key
185 def _check_variable(variable):
186 '''The loop variable should be a scalar integer. Check that this is
187 the case and raise an exception if not.
189 :param variable: the loop iterator.
190 :type variable: :py:class:`psyclone.psyir.symbols.DataSymbol`
192 :raises GenerationError: if the supplied variable is not a \
197 variable_name = f
"'{variable.name}'"
198 except AttributeError:
199 variable_name =
"property"
200 if not isinstance(variable, DataSymbol):
202 f
"variable {variable_name} in Loop class should be a "
203 f
"DataSymbol but found '{type(variable).__name__}'.")
204 if not isinstance(variable.datatype, ScalarType):
206 f
"variable {variable_name} in Loop class should be a "
207 f
"ScalarType but found '{type(variable.datatype).__name__}'.")
208 if variable.datatype.intrinsic != ScalarType.Intrinsic.INTEGER:
210 f
"variable {variable_name} in Loop class should be a "
211 f
"scalar integer but found "
212 f
"'{variable.datatype.intrinsic.name}'.")
215 def _validate_child(position, child):
217 :param int position: the position to be validated.
218 :param child: a child to be validated.
219 :type child: :py:class:`psyclone.psyir.nodes.Node`
221 :return: whether the given child and position are valid for this node.
225 return (position
in (0, 1, 2)
and isinstance(child, DataNode))
or (
226 position == 3
and isinstance(child, Schedule))
229 def create(cls, variable, start, stop, step, children):
231 '''Create a Loop instance given valid instances of a variable,
232 start, stop and step nodes, and a list of child nodes for the
235 :param variable: the PSyIR node containing the variable \
236 of the loop iterator.
237 :type variable: :py:class:`psyclone.psyir.symbols.DataSymbol`
238 :param start: the PSyIR node determining the value for the \
240 :type start: :py:class:`psyclone.psyir.nodes.Node`
241 :param end: the PSyIR node determining the value for the end \
243 :type end: :py:class:`psyclone.psyir.nodes.Node`
244 :param step: the PSyIR node determining the value for the loop \
246 :type step: :py:class:`psyclone.psyir.nodes.Node`
247 :param children: a list of PSyIR nodes contained in the \
249 :type children: list of :py:class:`psyclone.psyir.nodes.Node`
251 :returns: a Loop instance.
252 :rtype: :py:class:`psyclone.psyir.nodes.Loop`
254 :raises GenerationError: if the arguments to the create method \
255 are not of the expected type.
260 if not isinstance(children, list):
262 f
"children argument in create method of Loop class "
263 f
"should be a list but found '{type(children).__name__}'.")
265 loop = cls(variable=variable)
266 schedule =
Schedule(parent=loop, children=children)
267 loop.children = [start, stop, step, schedule]
270 def _check_completeness(self):
271 ''' Check that the Loop has 4 children and the 4th is a Schedule.
273 :raises InternalError: If the loop does not have 4 children or the
274 4th one is not a Schedule
281 f
"Loop is incomplete. It should have exactly 4 "
282 f
"children, but found loop with "
283 f
"'{', '.join([str(child) for child in self.children])}'.")
288 :returns: the PSyIR Node representing the Loop start expression.
289 :rtype: :py:class:`psyclone.psyir.nodes.Node`
297 ''' Setter for Loop start_expr attribute.
299 :param expr: New PSyIR start expression.
300 :type expr: :py:class:`psyclone.psyir.nodes.Node`
309 :returns: the PSyIR Node representing the Loop stop expression.
310 :rtype: :py:class:`psyclone.psyir.nodes.Node`
318 ''' Setter for Loop stop_expr attribute.
320 :param expr: New PSyIR stop expression.
321 :type expr: :py:class:`psyclone.psyir.nodes.Node`
330 :returns: the PSyIR Node representing the Loop step expression.
331 :rtype: :py:class:`psyclone.psyir.nodes.Node`
339 ''' Setter for Loop step_expr attribute.
341 :param expr: New PSyIR step expression.
342 :type expr: :py:class:`psyclone.psyir.nodes.Node`
351 :returns: the PSyIR Schedule with the loop body statements.
352 :rtype: :py:class:`psyclone.psyir.nodes.Schedule`
360 ''' Return the name to use in a dag for this node
362 :returns: Return the dag name for this loop
365 :raises InternalError: if this Loop has no ancestor Routine.
368 routine = self.
ancestorancestor(Routine)
370 raise InternalError(f
"Cannot generate DAG name for loop node "
371 f
"'{self}' because it is not contained within "
375 return "loop_" + str(position)
379 Returns the name of this node with (optional) control codes
380 to generate coloured output in a terminal that supports it.
382 :param bool colour: whether or not to include colour control codes.
384 :returns: description of this node, possibly coloured.
388 result = f
"{self.coloured_name(colour)}["
389 result += f
"variable='{self.variable.name}'"
391 result += f
", loop_type='{self.loop_type}'"
397 :returns: the control variable for this loop.
398 :rtype: :py:class:`psyclone.psyir.symbols.DataSymbol`
404 def variable(self, var):
406 Setter for the variable associated with this loop.
408 :param var: the control variable reference.
409 :type var: :py:class:`psyclone.psyir.symbols.DataSymbol`
417 name = self.__class__.__name__
419 result += f
"variable:'{self.variable.name}'"
421 result += f
", loop_type:'{self.loop_type}'"
424 result += str(entity) +
"\n"
425 result +=
"End " + name
429 '''Get all variable access information. It combines the data from
430 the loop bounds (start, stop and step), as well as the loop body.
431 The loop variable is marked as 'READ+WRITE' and references in start,
432 stop and step are marked as 'READ'.
434 :param var_accesses: VariablesAccessInfo instance that stores the \
435 information about variable accesses.
436 :type var_accesses: \
437 :py:class:`psyclone.core.VariablesAccessInfo`
449 AccessType.WRITE, self)
451 AccessType.READ, self)
457 var_accesses.next_location()
459 for child
in self.
loop_bodyloop_body.children:
460 child.reference_accesses(var_accesses)
461 var_accesses.next_location()
464 test_all_variables=False,
465 signatures_to_ignore=None,
467 '''This function analyses a loop in the PSyIR to see whether
468 its iterations are independent.
470 :param bool test_all_variables: if True, it will test if all variable
471 accesses are independent, otherwise it will stop after the first
472 variable access is found that isn't.
473 :param signatures_to_ignore: list of signatures for which to skip
475 :type signatures_to_ignore: Optional[
476 List[:py:class:`psyclone.core.Signature`]]
477 :param dep_tools: an optional instance of DependencyTools so that the
478 caller can access any diagnostic messages detailing why the loop
479 iterations are not independent.
480 :type dep_tools: Optional[
481 :py:class:`psyclone.psyir.tools.DependencyTools`]
483 :returns: True if the loop iterations are independent, False otherwise.
490 dtools = DependencyTools()
493 return dtools.can_loop_be_parallelised(
494 self, test_all_variables=test_all_variables,
495 signatures_to_ignore=signatures_to_ignore)
499 Generate the Fortran Loop and any associated code.
501 :param parent: the node in the f2pygen AST to which to add content.
502 :type parent: :py:class:`psyclone.f2pygen.SubroutineGen`
511 zero_reduction_variables(calls, parent)
521 for kernel
in self.
walkwalk(CodedKern):
522 if not kernel.module_inline:
524 kernel.rename_and_write()
538 for loop
in self.
walkwalk(Loop):
540 if loop._variable
is None:
545 name = loop.variable.name
546 kind = loop.variable.datatype.precision.name
547 kind_gen =
None if kind ==
"UNDEFINED" else kind
548 my_decl =
DeclGen(parent, datatype=
"integer",
554 for kernel
in self.
walkwalk(CodedKern):
555 if not kernel.module_inline:
556 parent.add(
UseGen(parent, name=kernel._module_name, only=
True,
557 funcnames=[kernel._name]))
def _check_variable(variable)
def create(cls, variable, start, stop, step, children)
def start_expr(self, expr)
def node_str(self, colour=True)
_loop_type_inference_rules
dictionary _loop_type_inference_rules
def gen_code(self, parent)
def reference_accesses(self, var_accesses)
def _check_completeness(self)
def set_loop_type_inference_rules(cls, rules)
def independent_iterations(self, test_all_variables=False, signatures_to_ignore=None, dep_tools=None)
def stop_expr(self, expr)
def step_expr(self, expr)
def children(self, my_children)
def reductions(self, reprod=None)
def is_openmp_parallel(self)
def _find_position(self, children, position=None)
def walk(self, my_type, stop_type=None, depth=None)
def ancestor(self, my_type, excluding=None, include_self=False, limit=None, shared_with=None)