38 '''Module providing a transformation from a PSyIR Array Range to a
39 PSyIR Loop. This could be useful for e.g. performance reasons, to
40 allow further transformations e.g. loop fusion or if the back-end does
41 not support array ranges.
43 By default the transformation will reject character arrays,
44 though this can be overriden by setting the
45 allow_string option to True. Note that PSyclone expresses syntax such
46 as `character(LEN=100)` as UnsupportedFortranType, and this
47 transformation will convert unknown or unsupported types to loops.
53 IntrinsicCall, Loop, Literal, Range, Reference
55 UnresolvedType, UnsupportedType, ArrayType, NoType
57 import TransformationError
61 '''Provides a transformation from a PSyIR Array Range to a PSyIR
64 >>> from psyclone.parse.algorithm import parse
65 >>> from psyclone.psyGen import PSyFactory
67 >>> filename = "tra_adv_compute.F90"
68 >>> ast, invoke_info = parse(filename, api=api)
69 >>> psy = PSyFactory(api).create(invoke_info)
70 >>> schedule = psy.invokes.invoke_list[0].schedule
72 >>> from psyclone.psyir.nodes import Assignment
73 >>> from psyclone.psyir.transformations import ArrayRange2LoopTrans, \
74 >>> TransformationError
76 >>> print(schedule.view())
77 >>> trans = ArrayRange2LoopTrans()
78 >>> for assignment in schedule.walk(Assignment):
81 >>> trans.apply(assignment)
82 >>> except TransformationError:
84 >>> print(schedule.view())
88 def apply(self, node, options=None):
89 '''Apply the ArrayRange2Loop transformation to the specified node. The
90 node must be an assignment. The rightmost range node in each array
91 within the assignment is replaced with a loop index and the
92 assignment is placed within a loop iterating over that
93 index. The bounds of the loop are determined from the bounds
94 of the array range on the left hand side of the assignment.
96 :param node: an Assignment node.
97 :type node: :py:class:`psyclone.psyir.nodes.Assignment`
98 :type options: Optional[Dict[str, Any]]
99 :param bool options["allow_string"]: whether to allow the
100 transformation on a character type array range. Defaults to False.
106 symbol_table = node.scope.symbol_table
107 loop_variable = symbol_table.new_symbol(
"idx", symbol_type=DataSymbol,
108 datatype=INTEGER_TYPE)
113 for array
in node.walk(ArrayReference):
114 for idx, child
in reversed(list(enumerate(array.children))):
115 if isinstance(child, Range):
116 if array
is node.lhs:
120 loop_variable, parent=array)
122 position = node.position
125 start, stop, step = lhs_range.pop_all_children()
126 loop = Loop.create(loop_variable, start, stop, step, [node.detach()])
127 parent.children.insert(position, loop)
130 return (
"Convert a PSyIR assignment to an array Range into a "
136 :returns: the name of the transformation as a string.
140 return type(self).__name__
143 '''Perform various checks to ensure that it is valid to apply the
144 ArrayRange2LoopTrans transformation to the supplied PSyIR Node.
146 By default the validate function will throw an TransofmrationError
147 on character arrays, though this can be overriden by setting the
148 allow_string option to True. Note that PSyclone expresses syntax such
149 as `character(LEN=100)` as UnsupportedFortranType, and this
150 transformation will convert unknown or unsupported types to loops.
152 :param node: the node that is being checked.
153 :type node: :py:class:`psyclone.psyir.nodes.Assignment`
154 :param options: a dictionary with options for transformations
155 :type options: Optional[Dict[str, Any]]
156 :param bool options["allow_string"]: whether to allow the
157 transformation on a character type array range. Defaults to False.
159 :raises TransformationError: if the node argument is not an
161 :raises TransformationError: if the node argument is an
162 Assignment whose left hand side is not an ArrayReference.
163 :raises TransformationError: if the node argument is an
164 Assignment whose left hand side is an ArrayReference that does
165 not have Range specifying the access to at least one of its
167 :raises TransformationError: if two or more of the loop ranges
168 in the assignment are different or are not known to be the
170 :raises TransformationError: if node contains a character type
171 child and the allow_strings option is
175 if not isinstance(node, Assignment):
176 raise TransformationError(
177 f
"Error in {self.name} transformation. The supplied node "
178 f
"argument should be a PSyIR Assignment, but found "
179 f
"'{type(node).__name__}'.")
181 if not isinstance(node.lhs, ArrayReference):
182 raise TransformationError(
183 f
"Error in {self.name} transformation. The lhs of the "
184 f
"supplied Assignment node should be a PSyIR ArrayReference, "
185 f
"but found '{type(node.lhs).__name__}'.")
187 if not [dim
for dim
in node.lhs.children
if isinstance(dim, Range)]:
188 raise TransformationError(
189 f
"Error in {self.name} transformation. The lhs of the supplied"
190 f
" Assignment node should be a PSyIR ArrayReference with at "
191 f
"least one of its dimensions being a Range, but found None "
201 for call
in node.rhs.walk(Call):
202 if isinstance(call, IntrinsicCall)
and call.intrinsic
in \
203 (IntrinsicCall.Intrinsic.LBOUND,
204 IntrinsicCall.Intrinsic.UBOUND):
206 raise TransformationError(
207 f
"Error in {self.name} transformation. The rhs of the supplied"
208 f
" Assignment contains a call '{call.debug_string()}'.")
212 for idx, child
in reversed(list(enumerate(node.lhs.children))):
213 if isinstance(child, Range):
220 for array
in node.walk(ArrayReference):
221 for idx, child
in reversed(list(enumerate(array.children))):
222 if isinstance(child, Range):
227 if not node.lhs.same_range(lhs_index, array, idx):
231 raise TransformationError(
232 f
"The ArrayRange2LoopTrans transformation only "
233 f
"supports ranges that are known to be the "
234 f
"same as each other but array access "
235 f
"'{node.lhs.name}' dimension {lhs_index} and "
236 f
"'{array.name}' dimension {idx} are either "
237 f
"different or can't be determined in the "
238 f
"assignment '{node}'.")
243 allow_string_array = options.get(
"allow_string",
False)
245 if not allow_string_array:
249 for child
in node.walk((Literal, Reference)):
252 if (isinstance(child.datatype,
253 (UnresolvedType, UnsupportedType, NoType))
254 or (isinstance(child.datatype, ArrayType)
and
255 isinstance(child.datatype.datatype,
256 (UnresolvedType, UnsupportedType)))):
258 if (child.datatype.intrinsic ==
259 ScalarType.Intrinsic.CHARACTER):
260 raise TransformationError(
261 "The ArrayRange2LoopTrans transformation doesn't "
262 "allow character arrays by default. This can be "
263 "enabled by passing the allow_string option to "
264 "the transformation."
266 except NotImplementedError:
271 'ArrayRange2LoopTrans']