Source code for psyclone.psyir.transformations.loop_tiling_2d_trans

# -----------------------------------------------------------------------------
# BSD 3-Clause License
#
# Copyright (c) 2021-2024, Science and Technology Facilities Council.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
# Authors: S. Siso and N. Nobre, STFC Daresbury Lab
# -----------------------------------------------------------------------------

'''This module provides the LoopTiling2DTrans, which transforms a 2D Loop
construct into a tiled implementation of the construct.'''

from psyclone.psyir.nodes import Loop
from psyclone.psyir.transformations.chunk_loop_trans import ChunkLoopTrans
from psyclone.psyir.transformations.loop_swap_trans import LoopSwapTrans
from psyclone.psyir.transformations.loop_trans import LoopTrans
from psyclone.psyir.transformations.transformation_error import \
    TransformationError


[docs]class LoopTiling2DTrans(LoopTrans): ''' Apply a 2D loop tiling transformation to a loop. For example: >>> from psyclone.psyir.frontend.fortran import FortranReader >>> from psyclone.psyir.nodes import Loop >>> from psyclone.psyir.transformations import LoopTiling2DTrans >>> psyir = FortranReader().psyir_from_source(""" ... subroutine sub() ... integer :: ji, tmp(100) ... do i=1, 100 ... do j=1, 100 ... tmp(i, j) = 2 * tmp(i, j) ... enddo ... enddo ... end subroutine sub""") >>> loop = psyir.walk(Loop)[0] >>> LoopTiling2DTrans().apply(loop) will generate: .. code-block:: fortran subroutine sub() integer :: ji integer, dimension(100) :: tmp integer :: ji_el_inner integer :: ji_out_var do i_out_var = 1, 100, 32 i_el_inner = MIN(i_out_var + (32 - 1), 100) do j_out_var = 1, 100, 32 do i = i_out_var, i_el_inner, 1 j_el_inner = MIN(j_out_var + (32 - 1), 100) do j = j_out_var, j_el_inner, 1 tmp(i, j) = 2 * tmp(i, j) enddo enddo enddo enddo end subroutine sub ''' def __str__(self): return "Tile the loop construct using 2D blocks" def validate(self, node, options=None): ''' Validates that the given Loop node can have a LoopTiling2DTrans applied. :param node: the loop to validate. :type node: :py:class:`psyclone.psyir.nodes.Loop` :param options: a dict with options for transformation. :type options: Optional[Dict[str, Any]] :param int options["tilesize"]: The size of the resulting tile, \ currently square tiles are always used. If not specified, the \ value 32 is used. :raises TransformationError: if an unsupported option has been \ provided. :raises TransformationError: if the provided tilesize is not a \ integer. ''' if options is None: options = {} super(LoopTiling2DTrans, self).validate(node, options=options) # Validate options map # TODO #613: Hardcoding the valid_options does not allow for # subclassing this transformation and adding new options, this # should be fixed. valid_options = ['tilesize'] for key, value in options.items(): if key in valid_options: if key == "tilesize" and not isinstance(value, int): raise TransformationError( f"The LoopTiling2DTrans tilesize option must be a " f"positive integer but found a " f"'{type(value).__name__}'.") if key == "tilesize" and value <= 0: raise TransformationError( f"The LoopTiling2DTrans tilesize option must be a " f"positive integer but found '{value}'.") else: raise TransformationError( f"The LoopTiling2DTrans does not support the " f"transformation option '{key}', the supported options " f"are: {valid_options}.") tilesize = options.get("tilesize", 32) # Even though the loops that ultimately will be swapped are the ones # resulting from the ChunkLoopTrans, these have the same validation # constrains as swapping the two original loops. This already # guarantees that we have a 2 loop construct with only one loop # statement inside the outer loop. LoopSwapTrans().validate(node) # Check that we can chunk both loops outer_loop = node inner_loop = node.loop_body.children[0] ChunkLoopTrans().validate(outer_loop, options={'chunksize': tilesize}) ChunkLoopTrans().validate(inner_loop, options={'chunksize': tilesize})
[docs] def apply(self, node, options=None): ''' Converts the given 2D Loop construct into a tiled version of the nested loops. :param node: the loop to transform. :type node: :py:class:`psyclone.psyir.nodes.Loop` :param options: a dict with options for transformations. :type options: Optional[Dict[str, Any]] :param int options["tilesize"]: The size of the resulting tile, \ currently square tiles are always used. If not \ specified, the value 32 is used. ''' self.validate(node, options) if options is None: options = {} tilesize = options.get("tilesize", 32) parent = node.parent position = node.position outer_loop = node inner_loop = node.loop_body.children[0] ChunkLoopTrans().apply(outer_loop, options={'chunksize': tilesize}) ChunkLoopTrans().apply(inner_loop, options={'chunksize': tilesize}) loops = parent[position].walk(Loop)[1] LoopSwapTrans().apply(loops)