Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/user_guide/transformations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,18 @@ can be found in the API-specific sections).

####

.. autoclass:: psyclone.psyir.transformations.Intrinsic2CodeTrans
:members: apply
:no-index:

####

.. autoclass:: psyclone.psyir.transformations.ArrayIntrinsic2LoopTrans
:members: apply
:no-index:

####

.. autoclass:: psyclone.psyir.transformations.IncreaseRankLoopArraysTrans
:members: apply
:no-index:
Expand Down
15 changes: 4 additions & 11 deletions examples/nemo/scripts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
from psyclone.psyir.symbols import DataSymbol, ArrayType
from psyclone.psyir.transformations import (
ArrayAssignment2LoopsTrans, HoistLoopBoundExprTrans, HoistLocalArraysTrans,
HoistTrans, InlineTrans, Maxval2LoopTrans, Sum2LoopTrans, Minval2LoopTrans,
Product2LoopTrans, ProfileTrans, OMPMinimiseSyncTrans,
HoistTrans, InlineTrans, ProfileTrans, OMPMinimiseSyncTrans,
Reference2ArrayRangeTrans, ScalarisationTrans, IncreaseRankLoopArraysTrans,
MaximalRegionTrans, TransformationError, DataNodeToTempTrans)
MaximalRegionTrans, TransformationError, DataNodeToTempTrans,
ArrayIntrinsic2LoopTrans)

# USE statements to chase to gather additional symbol information.
NEMO_MODULES_TO_IMPORT = [
Expand Down Expand Up @@ -186,14 +186,7 @@ def normalise_loops(
if loopify_array_intrinsics:
for intr in schedule.walk(IntrinsicCall):
try:
if intr.intrinsic.name == "MAXVAL":
Maxval2LoopTrans().apply(intr, verbose=True)
elif intr.intrinsic.name == "SUM":
Sum2LoopTrans().apply(intr, verbose=True)
elif intr.intrinsic.name == "MINVAL":
Minval2LoopTrans().apply(intr, verbose=True)
elif intr.intrinsic.name == "PRODUCT":
Product2LoopTrans().apply(intr, verbose=True)
ArrayIntrinsic2LoopTrans().apply(intr, verbose=True)
except TransformationError as err:
print(err.value)

Expand Down
6 changes: 5 additions & 1 deletion src/psyclone/psyGen.py
Original file line number Diff line number Diff line change
Expand Up @@ -2372,7 +2372,11 @@ def split_kwargs(self, **kwargs) -> tuple[dict[str, Any]]:
if key in trans.get_valid_options():
other_dicts[idx][key] = kwargs[key]
if key not in type(self).get_valid_options():
del first_dict[key]
# Sometimes we may have the same option in multiple
# subtransformations, so we only delete the key
# from the first_dict if it's still present.
if key in first_dict:
del first_dict[key]

return first_dict, *other_dicts

Expand Down
4 changes: 4 additions & 0 deletions src/psyclone/psyir/transformations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@
# Metatransformations
from psyclone.psyir.transformations.metatransformations.omp_cpu_routine_trans\
import OMPCPURoutineTrans
from psyclone.psyir.transformations.metatransformations.\
intrinsic2code_trans import Intrinsic2CodeTrans
from psyclone.psyir.transformations.metatransformations.\
arrayintrinsic2loop_trans import ArrayIntrinsic2LoopTrans

# For AutoAPI documentation generation
__all__ = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
'''
import warnings

from psyclone.psyir.transformations.intrinsics.intrinsic2code_trans import (
Intrinsic2CodeTrans)
from psyclone.psyir.transformations.intrinsics.intrinsic2code_basetrans \
import Intrinsic2CodeBaseTrans
from psyclone.psyir.nodes import (
BinaryOperation, Assignment, Reference, Literal, IfBlock, IntrinsicCall)
from psyclone.psyir.symbols import DataSymbol
from psyclone.utils import transformation_documentation_wrapper


@transformation_documentation_wrapper
class Abs2CodeTrans(Intrinsic2CodeTrans):
class Abs2CodeTrans(Intrinsic2CodeBaseTrans):
'''Provides a transformation from a PSyIR ABS Operator node to
equivalent code in a PSyIR tree. Validity checks are also
performed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,19 @@

# pylint: disable=too-many-locals

from psyclone.psyir.nodes import BinaryOperation, Assignment, Reference, \
Loop, Literal, ArrayReference, Range, Routine, IntrinsicCall
from psyclone.psyir.nodes import (
BinaryOperation, Assignment, Reference, Loop, Literal, ArrayReference,
Range, Routine, IntrinsicCall)
from psyclone.psyir.symbols import DataSymbol, ScalarType
from psyclone.psyir.transformations.transformation_error \
import TransformationError
from psyclone.psyir.transformations.intrinsics.intrinsic2code_trans import \
Intrinsic2CodeTrans
from psyclone.psyir.transformations.intrinsics.intrinsic2code_basetrans \
import Intrinsic2CodeBaseTrans
from psyclone.utils import transformation_documentation_wrapper


@transformation_documentation_wrapper
class DotProduct2CodeTrans(Intrinsic2CodeTrans):
class DotProduct2CodeTrans(Intrinsic2CodeBaseTrans):
'''Provides a transformation from a PSyIR DOT_PRODUCT Operator node to
equivalent code in a PSyIR tree. Validity checks are also
performed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@


@transformation_documentation_wrapper
class Intrinsic2CodeTrans(Transformation, metaclass=abc.ABCMeta):
class Intrinsic2CodeBaseTrans(Transformation, metaclass=abc.ABCMeta):
'''Provides support for transformations from PSyIR IntrinsicCall
nodes to equivalent PSyIR code in a PSyIR tree. Such
transformations can be useful when the intrinsic is not supported
Expand Down Expand Up @@ -114,4 +114,4 @@ def apply(self, node, options=None, **kwargs):


# For AutoAPI auto-documentation generation.
__all__ = ["Intrinsic2CodeTrans"]
__all__ = ["Intrinsic2CodeBaseTrans"]
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
Loop, Literal, ArrayReference, Range, IntrinsicCall)
from psyclone.psyir.symbols import (
DataSymbol, ScalarType, TypedSymbol, UnsupportedType)
from psyclone.psyir.transformations.intrinsics.intrinsic2code_trans import (
Intrinsic2CodeTrans)
from psyclone.psyir.transformations.intrinsics.intrinsic2code_basetrans \
import Intrinsic2CodeBaseTrans
from psyclone.utils import transformation_documentation_wrapper


Expand Down Expand Up @@ -65,7 +65,7 @@ def _create_array_ref(array_symbol, loop_idx_symbols, other_dims,


@transformation_documentation_wrapper
class Matmul2CodeTrans(Intrinsic2CodeTrans):
class Matmul2CodeTrans(Intrinsic2CodeBaseTrans):
'''Provides a transformation from a PSyIR MATMUL Operator node to
equivalent code in a PSyIR tree. Validity checks are also
performed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@
BinaryOperation, Assignment, Reference, IfBlock, IntrinsicCall
)
from psyclone.psyir.symbols import DataSymbol
from psyclone.psyir.transformations.intrinsics.intrinsic2code_trans import (
Intrinsic2CodeTrans
)
from psyclone.psyir.transformations.intrinsics.intrinsic2code_basetrans \
import Intrinsic2CodeBaseTrans
from psyclone.utils import transformation_documentation_wrapper


@transformation_documentation_wrapper
class MinOrMax2CodeTrans(Intrinsic2CodeTrans, ABC):
class MinOrMax2CodeTrans(Intrinsic2CodeBaseTrans, ABC):
'''Provides a utility transformation from a PSyIR MIN or MAX Intrinsic
node to equivalent code in a PSyIR tree. Validity checks are also
performed (by the parent class). This utility transformation is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
'''
import warnings

from psyclone.psyir.transformations.intrinsics.intrinsic2code_trans import (
Intrinsic2CodeTrans)
from psyclone.psyir.transformations.intrinsics.intrinsic2code_basetrans \
import Intrinsic2CodeBaseTrans
from psyclone.psyir.transformations import Abs2CodeTrans
from psyclone.psyir.nodes import (
BinaryOperation, Assignment, Reference, Literal, IfBlock, IntrinsicCall)
Expand All @@ -23,7 +23,7 @@


@transformation_documentation_wrapper
class Sign2CodeTrans(Intrinsic2CodeTrans):
class Sign2CodeTrans(Intrinsic2CodeBaseTrans):
'''Provides a transformation from a PSyIR SIGN intrinsic node to
equivalent code in a PSyIR tree. Validity checks are also
performed.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -----------------------------------------------------------------------------
# SPDX-FileCopyrightText: Copyright (c) 2026 Science and Technology
# Facilities Council
# SPDX-License-Identifier: BSD-3-Clause
# See the full LICENSE file in the project root for details.
# -----------------------------------------------------------------------------

'''This module contains the ArrayIntrinsic2LoopTrans metatransformation.'''

from psyclone.psyir.nodes import IntrinsicCall
from psyclone.psyir.transformations.intrinsics.maxval2loop_trans\
import Maxval2LoopTrans
from psyclone.psyir.transformations.intrinsics.minval2loop_trans\
import Minval2LoopTrans
from psyclone.psyir.transformations.intrinsics.product2loop_trans\
import Product2LoopTrans
from psyclone.psyir.transformations.intrinsics.sum2loop_trans\
import Sum2LoopTrans
from psyclone.psyir.transformations.metatransformations.intrinsic2code_trans\
import Intrinsic2CodeTrans
from psyclone.utils import transformation_documentation_wrapper


@transformation_documentation_wrapper
class ArrayIntrinsic2LoopTrans(Intrinsic2CodeTrans):
'''This metatransformation applies any of the Intrinsic2Loop
transformations to the provided input. The available transformations are
Maxval2LoopTrans, Sum2LoopTrans, Minval2LoopTrans, or Product2LoopTrans.
'''
_SUB_TRANSFORMATIONS = [Maxval2LoopTrans, Sum2LoopTrans,
Minval2LoopTrans, Product2LoopTrans]

# Create a map of intrinsic names to the appropriate Intrinsic2Code
# transformation. This should be in the same order as the
# _SUB_TRANSFORMATIONS else the _split_kwargs on this Transformation
# may not work correctly.
intrinsic_to_trans = {
IntrinsicCall.Intrinsic.MAXVAL: Maxval2LoopTrans,
IntrinsicCall.Intrinsic.SUM: Sum2LoopTrans,
IntrinsicCall.Intrinsic.MINVAL: Minval2LoopTrans,
IntrinsicCall.Intrinsic.PRODUCT: Product2LoopTrans}

def apply(self, node: IntrinsicCall, **kwargs) -> None:
'''
Applies the appropriate Intrinsic2Loop transformation to the provided
input node.

:param node: the IntrinsicCall to be transformed.
'''
# The apply function is required for the docstring wrapper to work
# correctly.
super().apply(node, **kwargs)


__all__ = ["ArrayIntrinsic2LoopTrans"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# -----------------------------------------------------------------------------
# SPDX-FileCopyrightText: Copyright (c) 2026 Science and Technology
# Facilities Council
# SPDX-License-Identifier: BSD-3-Clause
# See the full LICENSE file in the project root for details.
# -----------------------------------------------------------------------------

'''This module contains the Intrinsic2CodeTrans metatransformation.'''

from typing import Any
import logging

from psyclone.psyGen import Transformation
from psyclone.psyir.nodes import IntrinsicCall
from psyclone.psyir.transformations.intrinsics.abs2code_trans\
import Abs2CodeTrans
from psyclone.psyir.transformations.intrinsics.dotproduct2code_trans\
import DotProduct2CodeTrans
from psyclone.psyir.transformations.intrinsics.matmul2code_trans\
import Matmul2CodeTrans
from psyclone.psyir.transformations.intrinsics.max2code_trans\
import Max2CodeTrans
from psyclone.psyir.transformations.intrinsics.maxval2loop_trans\
import Maxval2LoopTrans
from psyclone.psyir.transformations.intrinsics.min2code_trans\
import Min2CodeTrans
from psyclone.psyir.transformations.intrinsics.minval2loop_trans\
import Minval2LoopTrans
from psyclone.psyir.transformations.intrinsics.product2loop_trans\
import Product2LoopTrans
from psyclone.psyir.transformations.intrinsics.sign2code_trans\
import Sign2CodeTrans
from psyclone.psyir.transformations.intrinsics.sum2loop_trans\
import Sum2LoopTrans
from psyclone.utils import transformation_documentation_wrapper


@transformation_documentation_wrapper
class Intrinsic2CodeTrans(Transformation):
'''This metatransformation applies any of the Intrinsic2Code
transformations to the provided input.
'''
_SUB_TRANSFORMATIONS = [Maxval2LoopTrans, Sum2LoopTrans,
Minval2LoopTrans, Product2LoopTrans,
DotProduct2CodeTrans, Abs2CodeTrans,
Max2CodeTrans, Min2CodeTrans,
Sign2CodeTrans, Matmul2CodeTrans]

# Create a map of intrinsic names to the appropriate Intrinsic2Code
# transformation. This should be in the same order as the
# _SUB_TRANSFORMATIONS else the _split_kwargs on this Transformation
# may not work correctly.
intrinsic_to_trans = {
IntrinsicCall.Intrinsic.MAXVAL: Maxval2LoopTrans,
IntrinsicCall.Intrinsic.SUM: Sum2LoopTrans,
IntrinsicCall.Intrinsic.MINVAL: Minval2LoopTrans,
IntrinsicCall.Intrinsic.PRODUCT: Product2LoopTrans,
IntrinsicCall.Intrinsic.DOT_PRODUCT: DotProduct2CodeTrans,
IntrinsicCall.Intrinsic.ABS: Abs2CodeTrans,
IntrinsicCall.Intrinsic.MAX: Max2CodeTrans,
IntrinsicCall.Intrinsic.MIN: Min2CodeTrans,
IntrinsicCall.Intrinsic.SIGN: Sign2CodeTrans,
IntrinsicCall.Intrinsic.MATMUL: Matmul2CodeTrans}

def validate(self, node: IntrinsicCall, **kwargs) -> None:
'''
Validates the input options.

:param node: the IntrinsicCall to be transformed.

:raises TypeError: if the input node is not an IntrinsicCall.
'''
# Validate the provided options are allowed and typed correctly.
self.validate_options(**kwargs)

if not isinstance(node, IntrinsicCall):
raise TypeError(
f"Input node to {self.name} must be an IntrinsicCall but "
f"received '{type(node).__name__}'."
)

def _split_kwargs(self, **kwargs) -> \
tuple[dict[str, Any],
dict[IntrinsicCall.Intrinsic, dict[str, Any]]]:
'''
:returns: the kwargs for this transformation and the kwargs dict for
the sub transformations indexed by appropriate Intrinsic.
'''
# The split_kwargs function returns a tuple containing the
# kwargs for this transformation as the first entry and then
# the kwargs for the SUB_TRANSFORAMTIONS in order as the following
# entries.
split_kwargs = self.split_kwargs(**kwargs)
local_kwargs = split_kwargs[0]
sub_kwargs = {}
for i, intrinsic in enumerate(self.intrinsic_to_trans):
sub_kwargs[intrinsic] = split_kwargs[i+1]
return local_kwargs, sub_kwargs

def apply(self, node: IntrinsicCall, **kwargs) -> None:
'''
Applies the appropriate Intrinsic2Code transformation to the provided
input node.

:param node: the IntrinsicCall to be transformed.
'''
# Split the options for the subtransformations. The options are
# returned in the order of the _SUB_TRANSFORMATIONS list.
kwargs_dict = {}
local_kwargs, kwargs_dict = self._split_kwargs(**kwargs)

self.validate(node, **local_kwargs)

# If the intrinsic is one of the supported intrinsics then
# apply the relevant transformation.
if node.intrinsic in self.intrinsic_to_trans:
self.intrinsic_to_trans[node.intrinsic]().apply(
node, **kwargs_dict[node.intrinsic]
)
else:
# Setup the logger.
logger = logging.getLogger(__name__)
supported_intrinsics = []
if logger.isEnabledFor(logging.INFO):
supported_intrinsics = [intrinsic.name for intrinsic in
self.intrinsic_to_trans.keys()]
logger.info(
f"Input node was intrinsic of type '{node.intrinsic.name}' "
f"which is not transformed by {self.name}. Supported "
f"intrinsics are {supported_intrinsics}."
)


__all__ = ["Intrinsic2CodeTrans"]
Loading
Loading