38 '''Module providing a transformation that given an Assignment node to an
39 ArrayReference in its left-hand-side which has at least one PSyIR Range
40 node (equivalent to an array assignment statement in Fortran), it converts it
41 to the equivalent explicit loop representation using a NemoLoop node.
49 Assignment, CodeBlock, ArrayMember, Routine, IntrinsicCall, \
50 StructureReference, StructureMember, Node, Literal
53 UnresolvedType, UnsupportedType, ArrayType, NoType
59 '''Transformation that given an assignment with an ArrayReference Range
60 in the LHS (equivalent to an array assignment statement in Fortran), it
61 converts it to an explicit loop doing each of the individual element
62 assignments separately. For example:
64 >>> from psyclone.parse.algorithm import parse
65 >>> from psyclone.psyGen import PSyFactory
67 >>> filename = "tra_adv.F90" # examples/nemo/code
68 >>> ast, invoke_info = parse(filename, api=api)
69 >>> psy = PSyFactory(api).create(invoke_info)
70 >>> schedule = psy.invokes.invoke_list[0].schedule
71 >>> print(schedule.view())
73 >>> from psyclone.psyir.nodes import Range
74 >>> from psyclone.domain.nemo.transformations import \
75 NemoArrayRange2LoopTrans
76 >>> from psyclone.transformations import TransformationError
78 >>> trans = NemoArrayRange2LoopTrans()
79 >>> for my_range in reversed(schedule.walk(Range)):
81 >>> trans.apply(my_range)
82 >>> except TransformationError:
84 >>> print(schedule.view())
86 The specified Range node must be the outermost Range (specifying
87 an access to an array index) within an Array Reference and the
88 array reference must be on the left-hand-side of an Assignment
89 node. This is required for correctness and if not satisfied the
90 transformation will raise an exception.
92 By default the transformation will reject character arrays,
93 though this can be overriden by setting the
94 allow_string option to True. Note that PSyclone expresses syntax such
95 as `character(LEN=100)` as UnsupportedFortranType, and this
96 transformation will convert unknown or unsupported types to loops.
99 def apply(self, node, options=None):
100 ''' Apply the transformation such that, given an assignment with an
101 ArrayReference Range in the LHS (equivalent to an array assignment
102 statement in Fortran), it converts it to an explicit loop doing each
103 of the individual element assignments separately.
105 The Range node is provided to the apply method of the transformation
106 to indicate which array index should be transformed. This can only
107 be applied to the outermost Range of the ArrayReference.
109 This is currently specific to the 'nemo' API in that it will create
112 :param node: a Range node.
113 :type node: :py:class:`psyclone.psyir.nodes.Range`
114 :param options: a dictionary with options for \
115 transformations. No options are used in this \
116 transformation. This is an optional argument that defaults \
118 :type options: Optional[Dict[str, Any]]
119 :param bool options["allow_string"]: whether to allow the
120 transformation on a character type array range. Defaults to False.
125 assignment = node.ancestor(Assignment)
126 parent = assignment.parent
128 symbol_table = node.ancestor(Routine).symbol_table
131 loop_variable_symbol = symbol_table.new_symbol(root_name=
"idx",
132 symbol_type=DataSymbol,
133 datatype=INTEGER_TYPE)
139 for top_level_ref
in assignment.walk(ArrayMixin, stop_type=ArrayMixin):
141 for array
in reversed(top_level_ref.walk(ArrayMixin)):
142 current_n_ranges = len([child
for child
in array.children
143 if isinstance(child, Range)])
144 if current_n_ranges == 0:
147 n_ranges = current_n_ranges
148 elif n_ranges != current_n_ranges:
150 "The number of ranges in the arrays within this "
151 "assignment are not equal. Any such case should have "
152 "been dealt with by the validation method or "
153 "represents invalid PSyIR.")
155 idx = array.get_outer_range_index()
156 array.children[idx] = Reference(loop_variable_symbol)
160 position = assignment.position
161 start, stop, step = node.pop_all_children()
162 loop = NemoLoop.create(loop_variable_symbol, start, stop, step,
163 [assignment.detach()])
164 parent.children.insert(position, loop)
168 "Convert the PSyIR assignment for a specified ArrayReference "
169 "Range into a PSyIR NemoLoop.")
174 :returns: the name of the transformation as a string.
178 return type(self).__name__
181 '''Perform various checks to ensure that it is valid to apply the
182 NemoArrayRange2LoopTrans transformation to the supplied PSyIR Node.
184 By default the validation will reject character arrays that PSyclone
185 understand as such, though this can be overriden by setting the
186 allow_string option to True. Note that PSyclone expresses syntax such
187 as `character(LEN=100)` as UnsupportedFortranType, and this
188 transformation will convert unknown or unsupported types to loops.
190 :param node: the node that is being checked.
191 :type node: :py:class:`psyclone.psyir.nodes.Range`
192 :param options: a dictionary with options for \
193 transformations. No options are used in this \
194 transformation. This is an optional argument that defaults \
196 :type options: Optional[Dict[str, Any]]
197 :param bool options["allow_string"]: whether to allow the
198 transformation on a character type array range. Defaults to False.
200 :raises TransformationError: if the node argument is not a \
201 Range, if the Range node is not part of an ArrayReference, \
202 if the Range node is not the outermost Range node of the \
203 ArrayReference or if that ArrayReference does not \
204 constitute the left hand side of an Assignment node.
205 :raises TransformationError: if the node argument has nested array \
206 expressions with Ranges or is an invalid tree with ranges in \
207 multiple locations of a structure of arrays.
208 :raises TransformationError: if the node argument contains a \
209 non-elemental Operation or Call.
210 :raises TransformationError: if node contains a character type
211 child and the allow_strings option is
216 if not isinstance(node, Range):
218 f
"Error in NemoArrayRange2LoopTrans transformation. The "
219 f
"supplied node argument should be a PSyIR Range, but "
220 f
"found '{type(node).__name__}'.")
222 if not node.parent
or not isinstance(node.parent, ArrayMixin):
224 f
"Error in NemoArrayRange2LoopTrans transformation. The "
225 f
"supplied node argument should be within an array access "
226 f
"node, but found '{type(node.parent).__name__}'.")
228 assignment = node.ancestor(Assignment)
231 f
"Error in NemoArrayRange2LoopTrans transformation. The "
232 f
"supplied node argument should be within an Assignment node, "
233 f
"but found a '{node}' that is not in an assignment.")
235 if node
not in assignment.lhs.walk(Node):
237 "Error in NemoArrayRange2LoopTrans transformation. The "
238 "supplied node argument should be within an array access "
239 "node that is within the left-hand-side of an Assignment "
240 "node, but it is on the right-hand-side.")
243 for range_expr
in assignment.walk(Range):
244 ancestor_array = range_expr.parent.ancestor(ArrayMixin)
245 if ancestor_array
and any(index.walk(Range)
for index
246 in ancestor_array.indices):
248 lambda: f
"Error in NemoArrayRange2LoopTrans transformation"
249 f
". This transformation does not support array assignments"
250 f
" that contain nested Range structures, but found:"
251 f
"\n{assignment.debug_string()}"))
254 nodes_to_check = assignment.walk((CodeBlock, Reference))
258 for cnode
in assignment.rhs.walk(Call):
259 nodes_to_check.remove(cnode.routine)
260 if isinstance(cnode, IntrinsicCall):
261 if cnode.intrinsic.is_inquiry:
263 name = cnode.intrinsic.name
264 type_txt =
"IntrinsicCall"
266 name = cnode.routine.name
268 if not cnode.is_elemental:
271 lambda: f
"Error in NemoArrayRange2LoopTrans "
272 f
"transformation. This transformation does not support non"
273 f
"-elemental {type_txt}s on the rhs of the associated "
274 f
"Assignment node, but found '{name}' in:\n"
275 f
"{assignment.debug_string()}'."))
278 if any(isinstance(n, CodeBlock)
for n
in nodes_to_check):
280 lambda: f
"Error in NemoArrayRange2LoopTrans transformation. "
281 f
"This transformation does not support array assignments that"
282 f
" contain a CodeBlock anywhere in the expression, but found:"
283 f
"\n{assignment.debug_string()}"))
285 references = [n
for n
in nodes_to_check
if isinstance(n, Reference)]
286 for reference
in references:
290 if isinstance(reference.parent, IntrinsicCall):
291 intrinsic = reference.parent.intrinsic
292 if intrinsic
is IntrinsicCall.Intrinsic.LBOUND:
294 if intrinsic
is IntrinsicCall.Intrinsic.UBOUND:
299 if isinstance(reference, (StructureReference, StructureMember)):
305 if isinstance(reference, (ArrayReference, ArrayMember)):
310 if not isinstance(reference.symbol, DataSymbol)
or \
311 not isinstance(reference.symbol.datatype, ScalarType):
313 f
"Error in NemoArrayRange2LoopTrans transformation. "
314 f
"Variable '{reference.symbol.name}' must be a DataSymbol"
315 f
" of ScalarType, but it's a '{reference.symbol}'.")
319 for child
in node.parent.indices[node.position+1:]:
320 if isinstance(child, Range):
322 "Error in NemoArrayRange2LoopTrans transformation. This "
323 "transformation can only be applied to the outermost "
328 allow_string_array = options.get(
"allow_string",
False)
330 if not allow_string_array:
334 for child
in assignment.walk((Literal, Reference)):
337 if (isinstance(child.datatype,
338 (UnresolvedType, UnsupportedType, NoType))
339 or (isinstance(child.datatype, ArrayType)
and
340 isinstance(child.datatype.datatype,
341 (UnresolvedType, UnsupportedType)))):
343 if (child.datatype.intrinsic ==
344 ScalarType.Intrinsic.CHARACTER):
346 "The NemoArrayRange2LoopTrans transformation "
347 "doesn't allow character arrays by default. This "
348 "can be enabled by passing the allow_string "
349 "option to the transformation."
351 except NotImplementedError:
357 'NemoArrayRange2LoopTrans']
def apply(self, node, options=None)
def validate(self, node, options=None)