39 ''' This module provides the KernelModuleInlineTrans transformation. '''
45 RoutineSymbol, DataSymbol, IntrinsicSymbol, DataTypeSymbol, Symbol,
46 ContainerSymbol, DefaultModuleInterface)
48 Container, ScopingNode, Reference, Routine, Literal, CodeBlock, Call)
52 ''' Module-inlines (bring the subroutine to the same compiler-unit) the
53 subroutine pointed by this Kernel. For example:
55 .. code-block:: python
57 from psyclone.domain.common.transformations import \\
58 KernelModuleInlineTrans
60 inline_trans = KernelModuleInlineTrans()
61 inline_trans.apply(schedule.walk(CodedKern)[0])
63 print(schedule.parent.view())
67 Not all kernel subroutines can be module-inlined. This transformation
68 will reject attempts to in-line kernels that access global data in the
74 return "Inline a kernel subroutine into the PSy module"
79 Checks that the supplied node is a Kernel and that it is possible to
82 :param kern: the kernel which is the target of the transformation.
83 :type kern: :py:class:`psyclone.psyGen.CodedKern`
84 :param options: a dictionary with options for transformations.
85 :type options: Optional[Dict[str, Any]]
87 :raises TransformationError: if the target node is not a sub-class of \
89 :raises TransformationError: if the subroutine containing the \
90 implementation of the kernel cannot be retrieved with \
91 'get_kernel_schedule'.
92 :raises TransformationError: if the name of the routine that \
93 implements the kernel is not the same as the kernel name. This \
94 will happen if the kernel is polymorphic (uses a Fortran \
95 INTERFACE) and will be resolved by #1824.
96 :raises TransformationError: if the kernel cannot be safely inlined.
99 if not isinstance(node, CodedKern):
100 raise TransformationError(
101 f
"Target of a {self.name} must be a sub-class of "
102 f
"psyGen.CodedKern but got '{type(node).__name__}'")
108 kernel_schedule = node.get_kernel_schedule()
109 except Exception
as error:
110 raise TransformationError(
111 f
"{self.name} failed to retrieve PSyIR for kernel "
112 f
"'{node.name}' using the 'get_kernel_schedule' method"
121 for var
in kernel_schedule.walk(Reference):
123 if isinstance(symbol, IntrinsicSymbol):
125 if not symbol.is_import:
127 var.scope.symbol_table.lookup(
128 symbol.name, scope_limit=kernel_schedule)
129 except KeyError
as err:
130 raise TransformationError(
131 f
"Kernel '{node.name}' contains accesses to "
132 f
"'{symbol.name}' which is declared in the same "
133 f
"module scope. Cannot inline such a kernel.")
from err
134 for block
in kernel_schedule.walk(CodeBlock):
135 for name
in block.get_symbol_names():
137 block.scope.symbol_table.lookup(
138 name, scope_limit=kernel_schedule)
139 except KeyError
as err:
140 if not block.scope.symbol_table.lookup(name).is_import:
141 raise TransformationError(
142 f
"Kernel '{node.name}' contains accesses to "
143 f
"'{name}' in a CodeBlock that is declared in the "
144 f
"same module scope. "
145 f
"Cannot inline such a kernel.")
from err
149 symtab = kernel_schedule.ancestor(Container).symbol_table
150 for scope
in kernel_schedule.walk(ScopingNode):
151 for symbol
in scope.symbol_table.symbols:
152 for mod
in symtab.containersymbols:
153 if symbol.name == mod.name
and not \
154 isinstance(symbol, ContainerSymbol):
155 raise TransformationError(
156 f
"Kernel '{node.name}' cannot be module-inlined"
157 f
" because the subroutine shadows the symbol "
158 f
"name of the module container '{symbol.name}'.")
163 existing_symbol = node.scope.symbol_table.lookup(node.name)
165 existing_symbol =
None
166 if existing_symbol
and not isinstance(existing_symbol, RoutineSymbol):
167 raise TransformationError(
168 f
"Cannot module-inline subroutine '{node.name}' because "
169 f
"symbol '{existing_symbol}' with the same name already "
170 f
"exists and changing the name of module-inlined "
171 f
"subroutines is not supported yet.")
174 def _prepare_code_to_inline(code_to_inline):
175 '''Prepare the PSyIR tree to inline by bringing in to the subroutine
176 all referenced symbols so that the implementation is self contained.
178 TODO #2271 will improve this method and could potentially
179 avoid the need for debug_string() within get_kernel_schedule()
180 in dynamo0.3.py. Sergi suggests that we may be missing the
181 traversal of the declaration init expressions here and that
182 might solve the problem. I'm not so sure and explain why in
183 get_kernel_schedule() but still referencing this issue.
185 :param code_to_inline: the subroutine to module-inline.
186 :type code_to_inline: :py:class:`psyclone.psyir.node.Routine`
190 source_container = code_to_inline.ancestor(Container)
194 for scope
in code_to_inline.walk(ScopingNode):
195 for symbol
in scope.symbol_table.symbols:
196 all_symbols.add(symbol)
197 for reference
in code_to_inline.walk(Reference):
198 all_symbols.add(reference.symbol)
199 for literal
in code_to_inline.walk(Literal):
201 if isinstance(literal.datatype.precision, Symbol):
202 all_symbols.add(literal.datatype.precision)
203 for caller
in code_to_inline.walk(Call):
204 all_symbols.add(caller.routine.symbol)
205 for cblock
in code_to_inline.walk(CodeBlock):
206 for name
in cblock.get_symbol_names():
207 all_symbols.add(cblock.scope.symbol_table.lookup(name))
210 symbols_to_bring_in = set()
211 for symbol
in all_symbols:
212 if symbol.is_unresolved
or symbol.is_import:
216 symbols_to_bring_in.add(symbol)
217 if isinstance(symbol, DataSymbol):
219 if isinstance(symbol.datatype, DataTypeSymbol):
220 symbols_to_bring_in.add(symbol.datatype)
221 elif hasattr(symbol.datatype,
'precision'):
222 if isinstance(symbol.datatype.precision, Symbol):
223 symbols_to_bring_in.add(symbol.datatype.precision)
226 for symbol
in symbols_to_bring_in:
227 if symbol.name
not in code_to_inline.symbol_table:
228 code_to_inline.symbol_table.add(symbol)
230 if symbol.is_unresolved:
233 for mod
in source_container.symbol_table.containersymbols:
234 if mod.wildcard_import:
235 if mod.name
not in code_to_inline.symbol_table:
236 code_to_inline.symbol_table.add(mod)
238 code_to_inline.symbol_table.lookup(mod.name).\
239 wildcard_import =
True
240 elif symbol.is_import:
241 module_symbol = symbol.interface.container_symbol
242 if module_symbol.name
not in code_to_inline.symbol_table:
243 code_to_inline.symbol_table.add(module_symbol)
247 symbol.interface.container_symbol = \
248 code_to_inline.symbol_table.lookup(module_symbol.name)
250 def apply(self, node, options=None):
251 ''' Bring the kernel subroutine into this Container.
253 :param node: the kernel to module-inline.
254 :type node: :py:class:`psyclone.psyGen.CodedKern`
255 :param options: a dictionary with options for transformations.
256 :type options: Optional[Dict[str, Any]]
270 code_to_inline = node.get_kernel_schedule()
271 name = code_to_inline.name
274 existing_symbol = node.scope.symbol_table.lookup(name)
276 existing_symbol =
None
280 if not existing_symbol:
283 node.ancestor(Container).symbol_table.add(RoutineSymbol(
284 name, interface=DefaultModuleInterface()
287 node.ancestor(Container).addchild(code_to_inline.detach())
291 for routine
in node.ancestor(Container).walk(Routine,
293 if routine.name == node.name:
299 if routine != code_to_inline:
300 raise TransformationError(
301 f
"Cannot inline subroutine '{node.name}' because "
302 f
"another, different, subroutine with the same "
303 f
"name already exists and versioning of module-"
304 f
"inlined subroutines is not implemented yet.")
309 if node.name.lower() != name:
316 node.module_inline =
True
def apply(self, node, options=None)
def validate(self, node, options=None)
def _prepare_code_to_inline(code_to_inline)