Reference Guide  2.5.0
psyclone.domain.common.transformations.kernel_module_inline_trans.KernelModuleInlineTrans Class Reference
Inheritance diagram for psyclone.domain.common.transformations.kernel_module_inline_trans.KernelModuleInlineTrans:
Collaboration diagram for psyclone.domain.common.transformations.kernel_module_inline_trans.KernelModuleInlineTrans:

Public Member Functions

def __str__ (self)
 
def validate (self, node, options=None)
 
def apply (self, node, options=None)
 
- Public Member Functions inherited from psyclone.psyGen.Transformation
def name (self)
 

Detailed Description

 Module-inlines (bring the subroutine to the same compiler-unit) the
subroutine pointed by this Kernel. For example:

.. code-block:: python

    from psyclone.domain.common.transformations import \\
            KernelModuleInlineTrans

    inline_trans = KernelModuleInlineTrans()
    inline_trans.apply(schedule.walk(CodedKern)[0])

    print(schedule.parent.view())


.. warning ::
    Not all kernel subroutines can be module-inlined. This transformation
    will reject attempts to in-line kernels that access global data in the
    original module.

Definition at line 51 of file kernel_module_inline_trans.py.

Member Function Documentation

◆ apply()

def psyclone.domain.common.transformations.kernel_module_inline_trans.KernelModuleInlineTrans.apply (   self,
  node,
  options = None 
)
 Bring the kernel subroutine into this Container.

:param node: the kernel to module-inline.
:type node: :py:class:`psyclone.psyGen.CodedKern`
:param options: a dictionary with options for transformations.
:type options: Optional[Dict[str, Any]]

Reimplemented from psyclone.psyGen.Transformation.

Definition at line 250 of file kernel_module_inline_trans.py.

250  def apply(self, node, options=None):
251  ''' Bring the kernel subroutine into this Container.
252 
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]]
257 
258  '''
259  self.validate(node, options)
260 
261  if not options:
262  options = {}
263 
264  # Note that we use the resolved callee subroutine name and not the
265  # caller one, this is important because if it is an interface it will
266  # use the concrete implementation name. When this happens the new name
267  # may already be in use, but the equality check below guarantees
268  # that if it exists it is only valid when it references the exact same
269  # implementation.
270  code_to_inline = node.get_kernel_schedule()
271  name = code_to_inline.name
272 
273  try:
274  existing_symbol = node.scope.symbol_table.lookup(name)
275  except KeyError:
276  existing_symbol = None
277 
278  self._prepare_code_to_inline(code_to_inline)
279 
280  if not existing_symbol:
281  # If it doesn't exist already, module-inline the subroutine by:
282  # 1) Registering the subroutine symbol in the Container
283  node.ancestor(Container).symbol_table.add(RoutineSymbol(
284  name, interface=DefaultModuleInterface()
285  ))
286  # 2) Insert the relevant code into the tree.
287  node.ancestor(Container).addchild(code_to_inline.detach())
288  else:
289  # The routine symbol already exist, and we know from the validation
290  # that its a Routine. Now check if they are exactly the same.
291  for routine in node.ancestor(Container).walk(Routine,
292  stop_type=Routine):
293  if routine.name == node.name:
294  # This TransformationError happens here and not in the
295  # validation because it needs the symbols_to_bring_in
296  # applied to effectively compare both versions
297  # This will be fixed when module-inlining versioning is
298  # implemented.
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.")
305 
306  # We only modify the kernel call name after the equality check to
307  # ensure the apply will succeed and we don't leave with an inconsistent
308  # tree.
309  if node.name.lower() != name:
310  node.name = name
311 
312  # Set the module-inline flag to avoid generating the kernel imports
313  # TODO #1823. If the kernel imports were generated at PSy-layer
314  # creation time, we could just remove it here instead of setting a
315  # flag.
316  node.module_inline = True

References psyclone.domain.common.transformations.kernel_module_inline_trans.KernelModuleInlineTrans._prepare_code_to_inline(), psyclone.domain.lfric.kernel.lfric_kernel_metadata.LFRicKernelMetadata.validate(), psyclone.transformations.MoveTrans.validate(), psyclone.transformations.Dynamo0p3AsyncHaloExchangeTrans.validate(), psyclone.domain.common.transformations.alg_invoke_2_psy_call_trans.AlgInvoke2PSyCallTrans.validate(), psyclone.domain.common.transformations.alg_trans.AlgTrans.validate(), psyclone.domain.common.transformations.kernel_module_inline_trans.KernelModuleInlineTrans.validate(), psyclone.domain.common.transformations.raise_psyir_2_alg_trans.RaisePSyIR2AlgTrans.validate(), psyclone.domain.gocean.transformations.gocean_const_loop_bounds_trans.GOConstLoopBoundsTrans.validate(), psyclone.domain.gocean.transformations.gocean_move_iteration_boundaries_inside_kernel_trans.GOMoveIterationBoundariesInsideKernelTrans.validate(), psyclone.domain.gocean.transformations.gocean_opencl_trans.GOOpenCLTrans.validate(), psyclone.domain.gocean.transformations.raise_psyir_2_gocean_kern_trans.RaisePSyIR2GOceanKernTrans.validate(), psyclone.domain.lfric.transformations.lfric_alg_invoke_2_psy_call_trans.LFRicAlgInvoke2PSyCallTrans.validate(), psyclone.domain.lfric.transformations.raise_psyir_2_lfric_kern_trans.RaisePSyIR2LFRicKernTrans.validate(), psyclone.domain.nemo.transformations.create_nemo_invoke_schedule_trans.CreateNemoInvokeScheduleTrans.validate(), psyclone.domain.nemo.transformations.create_nemo_psy_trans.CreateNemoPSyTrans.validate(), psyclone.domain.nemo.transformations.nemo_allarrayrange2loop_trans.NemoAllArrayRange2LoopTrans.validate(), psyclone.domain.nemo.transformations.nemo_arrayrange2loop_trans.NemoArrayRange2LoopTrans.validate(), psyclone.domain.nemo.transformations.nemo_outerarrayrange2loop_trans.NemoOuterArrayRange2LoopTrans.validate(), psyclone.psyad.transformations.assignment_trans.AssignmentTrans.validate(), psyclone.psyGen.Transformation.validate(), psyclone.psyir.transformations.acc_update_trans.ACCUpdateTrans.validate(), psyclone.psyir.transformations.allarrayaccess2loop_trans.AllArrayAccess2LoopTrans.validate(), psyclone.psyir.transformations.arrayaccess2loop_trans.ArrayAccess2LoopTrans.validate(), psyclone.psyir.transformations.arrayrange2loop_trans.ArrayRange2LoopTrans.validate(), psyclone.psyir.transformations.chunk_loop_trans.ChunkLoopTrans.validate(), psyclone.psyir.transformations.fold_conditional_return_expressions_trans.FoldConditionalReturnExpressionsTrans.validate(), psyclone.psyir.transformations.hoist_local_arrays_trans.HoistLocalArraysTrans.validate(), psyclone.psyir.transformations.hoist_loop_bound_expr_trans.HoistLoopBoundExprTrans.validate(), psyclone.psyir.transformations.hoist_trans.HoistTrans.validate(), psyclone.psyir.transformations.inline_trans.InlineTrans.validate(), psyclone.psyir.transformations.intrinsics.array_reduction_base_trans.ArrayReductionBaseTrans.validate(), psyclone.psyir.transformations.intrinsics.dotproduct2code_trans.DotProduct2CodeTrans.validate(), psyclone.psyir.transformations.intrinsics.intrinsic2code_trans.Intrinsic2CodeTrans.validate(), psyclone.psyir.transformations.intrinsics.matmul2code_trans.Matmul2CodeTrans.validate(), psyclone.psyir.transformations.loop_swap_trans.LoopSwapTrans.validate(), psyclone.psyir.transformations.loop_tiling_2d_trans.LoopTiling2DTrans.validate(), psyclone.psyir.transformations.loop_trans.LoopTrans.validate(), psyclone.psyir.transformations.omp_task_trans.OMPTaskTrans.validate(), psyclone.psyir.transformations.omp_taskwait_trans.OMPTaskwaitTrans.validate(), psyclone.psyir.transformations.parallel_loop_trans.ParallelLoopTrans.validate(), psyclone.psyir.transformations.reference2arrayrange_trans.Reference2ArrayRangeTrans.validate(), psyclone.psyir.transformations.replace_induction_variables_trans.ReplaceInductionVariablesTrans.validate(), psyclone.transformations.OMPDeclareTargetTrans.validate(), psyclone.transformations.DynamoOMPParallelLoopTrans.validate(), psyclone.transformations.Dynamo0p3OMPLoopTrans.validate(), psyclone.transformations.GOceanOMPLoopTrans.validate(), psyclone.transformations.Dynamo0p3RedundantComputationTrans.validate(), psyclone.transformations.Dynamo0p3KernelConstTrans.validate(), psyclone.transformations.ACCRoutineTrans.validate(), psyclone.transformations.KernelImportsToArguments.validate(), psyclone.domain.gocean.transformations.gocean_loop_fuse_trans.GOceanLoopFuseTrans.validate(), psyclone.domain.lfric.transformations.lfric_loop_fuse_trans.LFRicLoopFuseTrans.validate(), psyclone.psyir.transformations.loop_fuse_trans.LoopFuseTrans.validate(), psyclone.domain.gocean.transformations.gocean_extract_trans.GOceanExtractTrans.validate(), psyclone.domain.lfric.transformations.lfric_extract_trans.LFRicExtractTrans.validate(), psyclone.psyir.transformations.extract_trans.ExtractTrans.validate(), psyclone.psyir.transformations.nan_test_trans.NanTestTrans.validate(), psyclone.psyir.transformations.read_only_verify_trans.ReadOnlyVerifyTrans.validate(), psyclone.transformations.ParallelRegionTrans.validate(), psyclone.transformations.OMPParallelTrans.validate(), psyclone.transformations.ACCParallelTrans.validate(), psyclone.transformations.ACCKernelsTrans.validate(), psyclone.transformations.ACCDataTrans.validate(), psyclone.psyir.transformations.psy_data_trans.PSyDataTrans.validate(), psyclone.psyir.transformations.region_trans.RegionTrans.validate(), and psyclone.transformations.ACCEnterDataTrans.validate().

Here is the call graph for this function:

◆ validate()

def psyclone.domain.common.transformations.kernel_module_inline_trans.KernelModuleInlineTrans.validate (   self,
  node,
  options = None 
)
Checks that the supplied node is a Kernel and that it is possible to
inline its PSyIR.

:param kern: the kernel which is the target of the transformation.
:type kern: :py:class:`psyclone.psyGen.CodedKern`
:param options: a dictionary with options for transformations.
:type options: Optional[Dict[str, Any]]

:raises TransformationError: if the target node is not a sub-class of \
    psyGen.CodedKern.
:raises TransformationError: if the subroutine containing the \
    implementation of the kernel cannot be retrieved with \
    'get_kernel_schedule'.
:raises TransformationError: if the name of the routine that \
    implements the kernel is not the same as the kernel name. This \
    will happen if the kernel is polymorphic (uses a Fortran \
    INTERFACE) and will be resolved by #1824.
:raises TransformationError: if the kernel cannot be safely inlined.

Reimplemented from psyclone.psyGen.Transformation.

Definition at line 77 of file kernel_module_inline_trans.py.

77  def validate(self, node, options=None):
78  '''
79  Checks that the supplied node is a Kernel and that it is possible to
80  inline its PSyIR.
81 
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]]
86 
87  :raises TransformationError: if the target node is not a sub-class of \
88  psyGen.CodedKern.
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.
97 
98  '''
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__}'")
103 
104  # Check that the PSyIR and associated Symbol table of the Kernel is OK.
105  # If this kernel contains symbols that are not captured in the PSyIR
106  # SymbolTable then this raises an exception.
107  try:
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"
113  f" due to {error}."
114  ) from error
115 
116  # We do not support kernels that use symbols representing global
117  # variables declared in its own parent module (we would need to
118  # create new imports to this module for those, and we don't do
119  # this yet).
120  # These can only be found in References, Calls and CodeBlocks
121  for var in kernel_schedule.walk(Reference):
122  symbol = var.symbol
123  if isinstance(symbol, IntrinsicSymbol):
124  continue
125  if not symbol.is_import:
126  try:
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():
136  try:
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
146 
147  # We can't transform subroutines that shadow top-level symbol module
148  # names, because we won't be able to bring this into the subroutine
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}'.")
159 
160  # If the symbol already exist at the call site it must be referring
161  # to a Routine
162  try:
163  existing_symbol = node.scope.symbol_table.lookup(node.name)
164  except KeyError:
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.")
172 
Here is the caller graph for this function:

The documentation for this class was generated from the following file: