Source code for psyclone.psyir.nodes.omp_clauses

# -----------------------------------------------------------------------------
# BSD 3-Clause License
#
# Copyright (c) 2022-2025, 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 A. B. G. Chalk, STFC Daresbury Lab
# -----------------------------------------------------------------------------

''' This module contains the implementations of the various OpenMP Clause
nodes.'''

from enum import Enum
from psyclone.psyir.nodes.clause import Clause, OperatorClause
from psyclone.psyir.nodes.literal import Literal
from psyclone.psyir.nodes.reference import Reference
from psyclone.psyir.symbols import Symbol


[docs] class OMPNowaitClause(Clause): ''' OpenMP nowait clause. Disable the implicit barrier at the end of the associated directive. ''' _children_valid_format = None _clause_string = "nowait"
[docs] class OMPGrainsizeClause(Clause): ''' OpenMP grainsize clause, used by OMPTaskloopDirective. Controls the grainsize of the associated directive. ''' _children_valid_format = "Literal" _clause_string = "grainsize" @staticmethod def _validate_child(position, child) -> bool: ''' Decides whether a given child and position are valid for this node. One child allowed, of type Literal. :param int position: the position to be validated. :param child: a child to be validated. :type child: :py:class:`psyclone.psyir.nodes.Node` :return: whether the given child and position are valid for this node. ''' if position == 0: return isinstance(child, Literal) return False
[docs] class OMPNumTasksClause(Clause): ''' OpenMP numtasks clause, used by OMPTaskloopDirective. Controls the number of tasks created by OpenMP for the associated directive. ''' _children_valid_format = "Literal" _clause_string = "num_tasks" @staticmethod def _validate_child(position, child) -> bool: ''' Decides whether a given child and position are valid for this node. One child allowed, of type Literal. :param int position: the position to be validated. :param child: a child to be validated. :type child: :py:class:`psyclone.psyir.nodes.Node` :return: whether the given child and position are valid for this node. ''' if position == 0: return isinstance(child, Literal) return False
[docs] class OMPNogroupClause(Clause): ''' OpenMP nogroup clause, used by OMPTaskloopDirective to disable the implicit Taskgroup associated with a Taskloop. ''' _children_valid_format = None _clause_string = "nogroup"
[docs] class OMPSharedClause(Clause): ''' OpenMP shared clause. This is used to declare variables as shared in an OpenMP region. ''' _children_valid_format = "Reference*" @property def _clause_string(self) -> str: ''' :returns: the string that represents this clause in OpenMP (i.e. "shared"). Returns an empty string to avoid generation of code if this clause has no children. ''' if len(self.children) > 0: return "shared" return "" @staticmethod def _validate_child(position, child) -> bool: ''' Decides whether a given child and position are valid for this node. Any number of Reference nodes are allowed. :param int position: the position to be validated. :param child: a child to be validated. :type child: :py:class:`psyclone.psyir.nodes.Node` :return: whether the given child and position are valid for this node. ''' return isinstance(child, Reference)
[docs] class OMPPrivateClause(Clause): ''' OpenMP private clause. This is used to declare variables as private to an OpenMP region. ''' _children_valid_format = "Reference*"
[docs] @staticmethod def create(symbols): ''' Create a OMPPrivateClause containing a Reference to each of the provided symbols as children. :param symbols: List of symbols to reference in the private clause. :type symbols: List[:py:class:`psyclone.psyir.symbols.Symbol`] :returns: A OMPPrivateClause referencing the provided symbols. :rtype: py:class:`psyclone.psyir.nodes.OMPPrivateClause` :raises TypeError: If the symbols argument is not a List that contains only PSyIR Symbols. ''' if not isinstance(symbols, list): raise TypeError( f"OMPPrivateClause expected the 'symbols' argument to be a " f"list, but found '{type(symbols).__name__}' instead.") for symbol in symbols: if not isinstance(symbol, Symbol): raise TypeError( f"OMPPrivateClause expected all the items in the 'symbols'" f" list to be PSyIR Symbols, but found a " f"'{type(symbol).__name__}'.") references = [Reference(symbol) for symbol in symbols] return OMPPrivateClause(children=references)
@property def _clause_string(self) -> bool: ''' :returns: the string that represents this clause in OpenMP (i.e. "private"). Returns an empty string to avoid generation of code if this clause has no children. ''' if len(self.children) > 0: return "private" return "" @staticmethod def _validate_child(position, child) -> bool: ''' Decides whether a given child and position are valid for this node. Any number of Reference nodes are allowed. :param int position: the position to be validated. :param child: a child to be validated. :type child: :py:class:`psyclone.psyir.nodes.Node` :return: whether the given child and position are valid for this node. ''' return isinstance(child, Reference)
[docs] class OMPFirstprivateClause(Clause): ''' OpenMP firstprivate clause. This is used to declare variables as firstprivate to an OpenMP region. ''' _children_valid_format = "Reference*"
[docs] @staticmethod def create(symbols): ''' Create an OMPFirstprivateClause containing a Reference to each of the provided symbols as children. :param symbols: List of symbols to reference in the firstprivate clause. :type symbols: List[:py:class:`psyclone.psyir.symbols.Symbol`] :returns: A OMPFirstprivateClause referencing the provided symbols. :rtype: py:class:`psyclone.psyir.nodes.OMPFirstprivateClause` :raises TypeError: If the symbols argument is not a List that contains only PSyIR Symbols. ''' if not isinstance(symbols, list): raise TypeError( f"OMPFirstprivateClause expected the 'symbols' argument to be " f"a list, but found '{type(symbols).__name__}' instead.") for symbol in symbols: if not isinstance(symbol, Symbol): raise TypeError( f"OMPFirstprivateClause expected all the items in the " f"'symbols' list to be PSyIR Symbols, but found a " f"'{type(symbol).__name__}'.") references = [Reference(symbol) for symbol in symbols] return OMPFirstprivateClause(children=references)
@property def _clause_string(self) -> str: ''' :returns: the string that represents this clause in OpenMP (i.e. "firstprivate"). Returns an empty string to avoid generation of code if this clause has no children. ''' if len(self.children) > 0: return "firstprivate" return "" @staticmethod def _validate_child(position, child) -> bool: ''' Decides whether a given child and position are valid for this node. Any number of Reference nodes are allowed. :param int position: the position to be validated. :param child: a child to be validated. :type child: :py:class:`psyclone.psyir.nodes.Node` :return: whether the given child and position are valid for this node. ''' return isinstance(child, Reference)
[docs] class OMPDefaultClause(Clause): ''' OpenMP Default clause. Used to determine the default declaration for variables used in an OpenMP region. :param clause_type: The default data-sharing attribute to be described by this clause. The default value is OMPDefaultClause.DefaultClauseTypes.SHARED. :param kwargs: additional keyword arguments provided to the PSyIR node. :type kwargs: unwrapped dict. :raises TypeError: if the supplied clause_type is not the correct type. '''
[docs] class DefaultClauseTypes(Enum): '''Enumeration of the different types of OMPDefaultClause supported in PSyclone.''' SHARED = 0 NONE = 1 FIRSTPRIVATE = 2
_children_valid_format = None def __init__(self, clause_type: DefaultClauseTypes = DefaultClauseTypes.SHARED, **kwargs): if not isinstance(clause_type, OMPDefaultClause.DefaultClauseTypes): raise TypeError( "OMPDefaultClause expected 'clause_type' argument of type " "OMPDefaultClause.DefaultClauseTypes but found " f"'{type(clause_type).__name__}'") self._clause_type = clause_type super().__init__(**kwargs) @property def clause_type(self) -> DefaultClauseTypes: ''' Gets the clause type value of this OMPDefaultClause :returns: the clause type for this OMPDefaultClause. ''' return self._clause_type @property def _clause_string(self) -> str: ''' :returns: the string that represents this clause in OpenMP (e.g. "default(shared)"). The value in parentheses is dependent on the DefaultClauseTypes value in _clause_type. ''' clause_string = "default(" + str(self._clause_type.name).lower() + ")" return clause_string
[docs] def node_str(self, colour=True) -> str: ''' :param bool colour: whether or not to include control codes for coloured text. :returns: a text description of this node. ''' return self.coloured_name(colour) + f"[default={self._clause_type}]"
[docs] class OMPScheduleClause(Clause): ''' OpenMP Schedule clause used for OMP Do Directives. :param schedule: The OpenMP schedule to use with this directive. The default value is "none" which means that no explicit schedule is specified. :param kwargs: additional keyword arguments provided to the PSyIR node. :type kwargs: unwrapped dict. ''' _children_valid_format = "None" VALID_OMP_SCHEDULES = ["runtime", "static", "dynamic", "guided", "auto", "none"] def __init__(self, schedule: str = "none", **kwargs): self.schedule = schedule super().__init__(**kwargs) @property def _clause_string(self) -> str: ''' :returns: the string that represents this clause in OpenMP (e.g. "schedule(static)"). The value inside parentheses is set to the value of the schedule of this clause unless that value is 'none' in which case an empty string is returned. ''' if self._schedule != "none": return f"schedule({self._schedule})" return "" @property def schedule(self) -> str: ''' :returns: the schedule for this OMPScheduleClause. ''' return self._schedule @schedule.setter def schedule(self, schedule): ''' :param str schedule: the schedule to use for this clause. :raises ValueError: if the supplied value is not a recognised OpenMP schedule. ''' if schedule not in self.VALID_OMP_SCHEDULES: raise ValueError( f"Schedule must be one of {self.VALID_OMP_SCHEDULES}. " f"Found '{schedule}'.") self._schedule = schedule def __eq__(self, other) -> bool: ''' Two OMPScheduleClause are equal if they have the same schedule. :param object other: the object to check equality to. :returns: whether other is equal to self. ''' is_eq = super().__eq__(other) is_eq = is_eq and (self.schedule == other.schedule) return is_eq
[docs] def node_str(self, colour: bool = True) -> str: ''' :param bool colour: whether or not to include control codes for coloured text. :returns: a text description of this node. ''' return self.coloured_name(colour) + f"[schedule={self._schedule}]"
[docs] class OMPDependClause(OperatorClause): ''' OpenMP Depend clause used for OpenMP Task directives. :param depend_type: The dependency type to use for this clause. The default value is OMPDependClause.DependClauseTypes.INOUT. :param kwargs: additional keyword arguments provided to the PSyIR node. :type kwargs: unwrapped dict. :raises TypeError: if the supplied depend_type argument is the wrong type. ''' _children_valid_format = "Reference*" _clause_string = "depend"
[docs] class DependClauseTypes(Enum): '''Enumeration of the different types of OMPDependClause supported in PSyclone.''' IN = 0 OUT = 1 INOUT = 2
def __init__(self, depend_type: DependClauseTypes = DependClauseTypes.INOUT, **kwargs): if not isinstance(depend_type, OMPDependClause.DependClauseTypes): raise TypeError( "OMPDependClause expected 'depend_type' argument of type " "OMPDependClause.DependClauseTypes but found " f"'{type(depend_type).__name__}'") self._operator = depend_type super().__init__(**kwargs) @staticmethod def _validate_child(position, child) -> bool: ''' Decides whether a given child and position are valid for this node. Any number of children allowed, but must be Reference. :param int position: the position to be validated. :param child: a child to be validated. :type child: :py:class:`psyclone.psyir.nodes.Node` :return: whether the given child and position are valid for this node. ''' return isinstance(child, Reference) @property def operator(self) -> DependClauseTypes: ''' :returns: the operator of this clause. ''' return self._operator def __eq__(self, other) -> bool: '''Two OMPDependClause are equal if: 1. Same type (OMPDependClause). 2. Same Operator 3. Same number of children. 4. Their children are equal. :param object other: the object to check equality to. :returns: whether other is equal to self. ''' is_eq = super().__eq__(other) is_eq = is_eq and (self.operator == other.operator) return is_eq
[docs] def node_str(self, colour=True) -> str: ''' :param bool colour: whether or not to include control codes for coloured text. :returns: a text description of this node. ''' return (f"{self.coloured_name(colour)}" f"[operator={str(self._operator)}]")
[docs] class OMPReductionClause(OperatorClause): ''' OpenMP Reduction clause. :param operator: The reduction operator to use for this clause. :param kwargs: additional keyword arguments provided to the PSyIR node. :type kwargs: unwrapped dict. :raises TypeError: if the supplied operator argument is the wrong type. ''' _children_valid_format = "Reference+" _clause_string = "reduction"
[docs] class ReductionClauseTypes(Enum): '''Enumeration of the different operators for OMPReductionClause supported in PSyclone.''' # Arithmetic operators ADD = 0 SUB = 1 MUL = 2 # Logical operators AND = 3 OR = 4 EQV = 5 # Fortran specific NEQV = 6 # Fortran specific # Intrinsic procedures MAX = 7 MIN = 8 IAND = 9 IOR = 10 IEOR = 11
def __init__(self, operator: ReductionClauseTypes, **kwargs): if not isinstance(operator, OMPReductionClause.ReductionClauseTypes): raise TypeError( "OMPReductionClause expected 'operator' argument of type " "OMPReductionClause.ReductionClauseTypes but found " f"'{type(operator).__name__}'") self._operator = operator super().__init__(**kwargs) @staticmethod def _validate_child(position, child) -> bool: ''' Decides whether a given child and position are valid for this node. Any number of children allowed, but must be Reference. :param int position: the position to be validated. :param child: a child to be validated. :type child: :py:class:`psyclone.psyir.nodes.Node` :return: whether the given child and position are valid for this node. ''' return isinstance(child, Reference) @property def operator(self) -> ReductionClauseTypes: ''' :returns: the operator of this clause. ''' return self._operator def __eq__(self, other) -> bool: '''Two OMPReductionClause are equal if: 1. Same type (OMPReductionClause). 2. Same Operator 3. Same number of children. 4. Their children are equal. :param object other: the object to check equality to. :returns: whether other is equal to self. ''' is_eq = super().__eq__(other) is_eq = is_eq and (self.operator == other.operator) return is_eq
[docs] def node_str(self, colour: bool = True) -> str: ''' :param bool colour: whether or not to include control codes for coloured text. :returns: a text description of this node. ''' return (f"{self.coloured_name(colour)}" f"[operator={str(self._operator.name).lower()}: " f"{str(self.children)}]")