38 '''This module contains the GOConstLoopBoundsTrans.'''
51 ''' Use of a common constant variable for each loop bound within
52 a GOInvokeSchedule. By deafault, PSyclone generates loops where
53 the bounds are obtained by de-referencing a field object, e.g.:
55 .. code-block:: fortran
57 DO j = my_field%grid%internal%ystart, my_field%grid%internal%ystop
59 Some compilers are able to produce more efficient code if they are
60 provided with information on the relative trip-counts of the loops
61 within an Invoke. With constant loop bounds, PSyclone generates code
64 .. code-block:: fortran
66 ny = my_field%grid%subdomain%internal%ystop
70 In practice, the application of the constant loop bounds transformation
71 looks something like, e.g.:
73 >>> from psyclone.parse.algorithm import parse
74 >>> from psyclone.psyGen import PSyFactory
76 >>> TEST_API = "gocean1.0"
77 >>> _, info = parse(os.path.join("tests", "test_files", "gocean1p0",
78 ... "single_invoke.f90"),
80 >>> psy = PSyFactory(TEST_API).create(info)
81 >>> invoke = psy.invokes.get('invoke_0_compute_cu')
82 >>> schedule = invoke.schedule
84 >>> from psyclone.transformations import GOConstLoopBoundsTrans
85 >>> clbtrans = GOConstLoopBoundsTrans()
87 >>> clbtrans.apply(schedule)
88 >>> print(schedule.view())
92 return "Use constant loop bounds for all loops in a GOInvokeSchedule"
97 :returns: the name of the Transformation as a string.
101 return "GOConstLoopBoundsTrans"
104 '''Checks if it is valid to apply the GOConstLoopBoundsTrans
107 :param node: the GOInvokeSchedule to transform.
108 :type node: :py:class:`psyclone.gocean1p0.GOInvokeSchedule`
109 :param options: a dictionary with options for transformations.
110 :type options: Optional[Dict[str, Any]]
112 :raises TransformationError: if the supplied node is not a \
114 :raises TransformationError: if the supplied schedule has loops with \
115 a loop with loop_type different than 'inner' or 'outer'.
116 :raises TransformationError: if the supplied schedule has loops with \
117 attributes for index_offsets, field_space, iteration_space and \
118 loop_type that don't appear in the GOLoop.bounds_lookup table.
119 :raises TransformationError: if the supplied schedule doesn't have a \
123 if not isinstance(node, GOInvokeSchedule):
124 raise TransformationError(
125 f
"GOConstLoopBoundsTrans can only be applied to "
126 f
"'GOInvokeSchedule' but found '{type(node).__name__}'.")
128 for loop
in node.walk(GOLoop):
129 if loop.loop_type
not in [
"inner",
"outer"]:
130 raise TransformationError(
131 f
"GOConstLoopBoundsTrans can not transform a loop with "
132 f
"loop_type '{loop.loop_type}', only 'inner' or 'outer' "
133 f
"loop_type values are expected.")
135 if loop.index_offset
not in loop.bounds_lookup:
136 raise TransformationError(
137 f
"GOConstLoopBoundsTrans can not transform a loop with "
138 f
"index_offset '{loop.index_offset}' because it is not in "
139 f
"the bounds lookup table, the available index_offset "
140 f
"values are {list(loop.bounds_lookup.keys())}.")
142 table = loop.bounds_lookup[loop.index_offset]
143 if loop.field_space
not in table:
144 raise TransformationError(
145 f
"GOConstLoopBoundsTrans can not transform a loop with "
146 f
"field_space '{loop.field_space}' because it is not in "
147 f
"the bounds lookup table, the available field_space "
148 f
"values are {list(table.keys())}.")
150 table = table[loop.field_space]
151 if loop.iteration_space
not in table:
152 raise TransformationError(
153 f
"GOConstLoopBoundsTrans can not transform a loop with "
154 f
"iteration_space '{loop.iteration_space}' because it is "
155 f
"not in the bounds lookup table, the available "
156 f
"iteration_space values are {list(table.keys())}.")
158 table = table[loop.iteration_space]
159 if loop.loop_type
not in table:
160 raise TransformationError(
161 f
"GOConstLoopBoundsTrans can not transform a loop with "
162 f
"loop_type '{loop.loop_type}' because it is not in the "
163 f
"bounds lookup table, the available loop_type values are "
164 f
"{list(table.keys())}.")
167 for arg
in node.symbol_table.argument_list:
168 if isinstance(arg.datatype, DataTypeSymbol):
169 if arg.datatype.name ==
"r2d_field":
172 raise TransformationError(
173 f
"GOConstLoopBoundsTrans can not transform invoke "
174 f
"'{node.name}' because it does not have any field arguments.")
176 def apply(self, node, options=None):
177 ''' Modify the GOcean kernel loops in a GOInvokeSchedule to use
178 common constant loop bound variables.
180 :param node: the GOInvokeSchedule of which all loops will get the \
181 constant loop bounds.
182 :type node: :py:class:`psyclone.gocean1p0.GOInvokeSchedule`
183 :param options: a dictionary with options for transformations.
184 :type options: Optional[Dict[str, Any]]
189 i_stop = node.symbol_table.new_symbol(
190 "istop", symbol_type=DataSymbol, datatype=INTEGER_TYPE)
191 j_stop = node.symbol_table.new_symbol(
192 "jstop", symbol_type=DataSymbol, datatype=INTEGER_TYPE)
196 for arg
in node.symbol_table.argument_list:
197 if isinstance(arg.datatype, DataTypeSymbol):
198 if arg.datatype.name ==
"r2d_field":
204 api_config = Config.get().api_conf(
"gocean1.0")
205 xstop = api_config.grid_properties[
"go_grid_xstop"].fortran \
207 ystop = api_config.grid_properties[
"go_grid_ystop"].fortran \
212 assign1 = Assignment.create(
214 StructureReference.create(
215 field, xstop.split(
'%')[1:]))
216 assign1.preceding_comment =
"Look-up loop bounds"
217 assign2 = Assignment.create(
219 StructureReference.create(
220 field, ystop.split(
'%')[1:]))
221 node.children.insert(0, assign1)
222 node.children.insert(1, assign2)
228 for loop
in node.walk(GOLoop):
231 if loop.loop_type ==
"inner":
233 elif loop.loop_type ==
"outer":
237 f
"Found a loop with loop_type '{loop.loop_type}' but the "
238 f
"only expected values are 'inner' or 'outer'.")
241 bounds = loop.bounds_lookup[loop.index_offset][loop.field_space][
242 loop.iteration_space][loop.loop_type]
245 start_expr = bounds[
"start"].format(start=
'2', stop=stop)
246 start_expr =
"".join(start_expr.split())
249 if start_expr ==
"2-1":
251 psyir = fortran_reader.psyir_from_expression(
252 start_expr, node.symbol_table)
253 loop.children[0].replace_with(psyir)
256 stop_expr = bounds[
"stop"].format(start=
'2', stop=stop)
257 stop_expr =
"".join(stop_expr.split())
258 if stop_expr ==
"2-1":
260 psyir = fortran_reader.psyir_from_expression(
261 stop_expr, node.symbol_table)
262 loop.children[1].replace_with(psyir)
def apply(self, node, options=None)
def validate(self, node, options=None)