psyclone.psyir.transformations

Transformation module, containing all generic (API independent) transformations and base classes.

Submodules

Classes

  • ACCUpdateTrans: Examines the supplied Schedule and adds OpenACC “update” directives

  • ArrayRange2LoopTrans: Provides a transformation from a PSyIR Array Range to a PSyIR

  • ChunkLoopTrans: Apply a chunking transformation to a loop (in order to permit a

  • ExtractTrans: This transformation inserts an ExtractNode or a node derived

  • FoldConditionalReturnExpressionsTrans: Provides a transformation that folds conditional expressions with only

  • HoistLocalArraysTrans: This transformation takes a Routine and promotes any local, ‘automatic’

  • HoistLoopBoundExprTrans: This transformation moves complex bounds expressions out of the loop

  • HoistTrans: This transformation takes an assignment and moves it outside of

  • InlineTrans: This transformation takes a Call (which may have a return value)

  • Abs2CodeTrans: Provides a transformation from a PSyIR ABS Operator node to

  • DotProduct2CodeTrans: Provides a transformation from a PSyIR DOT_PRODUCT Operator node to

  • Matmul2CodeTrans: Provides a transformation from a PSyIR MATMUL Operator node to

  • Max2CodeTrans: Provides a transformation from a PSyIR MAX Intrinsic node to

  • Min2CodeTrans: Provides a transformation from a PSyIR MIN Intrinsic node to

  • Sign2CodeTrans: Provides a transformation from a PSyIR SIGN intrinsic node to

  • Sum2LoopTrans: Provides a transformation from a PSyIR SUM IntrinsicCall node to an

  • LoopFuseTrans: Provides a generic loop-fuse transformation to two Nodes in the

  • LoopSwapTrans: Provides a loop-swap transformation, e.g.:

  • LoopTiling2DTrans: Apply a 2D loop tiling transformation to a loop. For example:

  • LoopTrans: This abstract class is a base class for all transformations that act

  • Maxval2LoopTrans: Provides a transformation from a PSyIR MAXVAL IntrinsicCall node to

  • Minval2LoopTrans: Provides a transformation from a PSyIR MINVAL IntrinsicCall node to

  • NanTestTrans: This transformation inserts a NanTestNode into the PSyIR of a

  • OMPLoopTrans: Adds an OpenMP directive to parallelise this loop. It can insert different

  • OMPTargetTrans: Adds an OpenMP target directive to a region of code.

  • OMPTaskTrans: Apply an OpenMP Task Transformation to a Loop. The Loop must

  • OMPTaskwaitTrans: Adds zero or more OpenMP Taskwait directives to an OMP parallel region.

  • ParallelLoopTrans: Adds an abstract directive (it needs to be specified by sub-classing this

  • Product2LoopTrans: Provides a transformation from a PSyIR PRODUCT IntrinsicCall node to

  • ProfileTrans: Create a profile region around a list of statements. For

  • PSyDataTrans: Create a PSyData region around a list of statements. For

  • ReadOnlyVerifyTrans: This transformation inserts a ReadOnlyVerifyNode or a node derived

  • Reference2ArrayRangeTrans: Provides a transformation from PSyIR Array Notation (a reference to

  • RegionTrans: This abstract class is a base class for all transformations that act

  • ReplaceInductionVariablesTrans: Move all supported induction variables out of the loop, and replace

class psyclone.psyir.transformations.ACCUpdateTrans

Examines the supplied Schedule and adds OpenACC “update” directives for any data accessed outside of a kernels or parallel region. For example:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.nodes import Routine
>>> from psyclone.psyir.transformations import ACCUpdateTrans
>>>
>>> code = """
... subroutine run_it()
...   real :: a(10)
...   a(:) = 7.0
... end subroutine run_it"""
>>>
>>> psyir = FortranReader().psyir_from_source(code)
>>> routine = psyir.walk(Routine)[0]
>>>
>>> # Add update directives
>>> uptrans = ACCUpdateTrans()
>>> uptrans.apply(routine)
>>>
>>> # Uncomment to see a text view of the new routine schedule
>>> # print(routine.view())
>>> print(FortranWriter()(routine))
subroutine run_it()
  real, dimension(10) :: a

  !$acc update if_present host(a)
  a(:) = 7.0
  !$acc update if_present device(a)

end subroutine run_it

Inheritance

Inheritance diagram of ACCUpdateTrans
apply(node, options=None)

Applies this transformation to the supplied Schedule. Identifies any regions of code outside of OpenACC regions and adds the necessary OpenACC update directives to ensure that the host and device copies of any variables are kept up-to-date.

Parameters:
  • node (psyclone.psyir.nodes.Schedule) – the Schedule that is to be transformed.

  • options (Optional[Dict[str, Any]]) – any options to this transformation.

validate(node, options=None)

Checks that it is valid to apply this transformation to the supplied schedule.

Parameters:
  • node (psyclone.psyir.nodes.Schedule) – the Schedule that is to be transformed.

  • options (Optional[Dict[str, Any]]) – any options to this transformation.

Raises:
class psyclone.psyir.transformations.ArrayRange2LoopTrans

Provides a transformation from a PSyIR Array Range to a PSyIR Loop. For example:

>>> from psyclone.parse.algorithm import parse
>>> from psyclone.psyGen import PSyFactory
>>> api = "nemo"
>>> filename = "tra_adv_compute.F90"
>>> ast, invoke_info = parse(filename, api=api)
>>> psy = PSyFactory(api).create(invoke_info)
>>> schedule = psy.invokes.invoke_list[0].schedule
>>>
>>> from psyclone.psyir.nodes import Assignment
>>> from psyclone.psyir.transformations import ArrayRange2LoopTrans,     >>>     TransformationError
>>>
>>> print(schedule.view())
>>> trans = ArrayRange2LoopTrans()
>>> for assignment in schedule.walk(Assignment):
>>>     while True:
>>>         try:
>>>             trans.apply(assignment)
>>>         except TransformationError:
>>>             break
>>> print(schedule.view())

Inheritance

Inheritance diagram of ArrayRange2LoopTrans
apply(node, options=None)

Apply the ArrayRange2Loop transformation to the specified node. The node must be an assignment. The rightmost range node in each array within the assignment is replaced with a loop index and the assignment is placed within a loop iterating over that index. The bounds of the loop are determined from the bounds of the array range on the left hand side of the assignment.

Parameters:

node (psyclone.psyir.nodes.Assignment) – an Assignment node.

property name
Returns:

the name of the transformation as a string.

Return type:

str

validate(node, options=None)

Perform various checks to ensure that it is valid to apply the ArrayRange2LoopTrans transformation to the supplied PSyIR Node.

Parameters:

node (psyclone.psyir.nodes.Assignment) – the node that is being checked.

Raises:
  • TransformationError – if the node argument is not an Assignment.

  • TransformationError – if the node argument is an Assignment whose left hand side is not an ArrayReference.

  • TransformationError – if the node argument is an Assignment whose left hand side is an ArrayReference that does not have Range specifying the access to at least one of its dimensions.

  • TransformationError – if two or more of the loop ranges in the assignment are different or are not known to be the same.

class psyclone.psyir.transformations.ChunkLoopTrans

Apply a chunking transformation to a loop (in order to permit a chunked parallelisation). For example:

>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.nodes import Loop
>>> from psyclone.psyir.transformations import ChunkLoopTrans
>>> psyir = FortranReader().psyir_from_source("""
... subroutine sub()
...     integer :: ji, tmp(100)
...     do ji=1, 100
...         tmp(ji) = 2 * ji
...     enddo
... end subroutine sub""")
>>> loop = psyir.walk(Loop)[0]
>>> ChunkLoopTrans().apply(loop)

will generate:

subroutine sub()
    integer :: ji
    integer, dimension(100) :: tmp
    integer :: ji_el_inner
    integer :: ji_out_var
    do ji_out_var = 1, 100, 32
        ji_el_inner = MIN(ji_out_var + (32 - 1), 100)
        do ji = ji_out_var, ji_el_inner, 1
            tmp(ji) = 2 * ji
        enddo
    enddo
end subroutine sub

Inheritance

Inheritance diagram of ChunkLoopTrans
apply(node, options=None)

Converts the given Loop node into a nested loop where the outer loop is over chunks and the inner loop is over each individual element of the chunk.

Parameters:
  • node (psyclone.psyir.nodes.Loop) – the loop to transform.

  • options (Optional[Dict[str, Any]]) – a dict with options for transformations.

  • options["chunksize"] (int) – The size to chunk over for this transformation. If not specified, the value 32 is used.

validate(node, options=None)

Validates that the given Loop node can have a ChunkLoopTrans applied.

Parameters:
  • node (psyclone.psyir.nodes.Loop) – the loop to validate.

  • options (Optional[Dict[str, Any]]) – a dict with options for transformation.

  • options["chunksize"] (int) – The size to chunk over for this transformation. If not specified, the value 32 is used.

Raises:
class psyclone.psyir.transformations.ExtractTrans(node_class=<class 'psyclone.psyir.nodes.extract_node.ExtractNode'>)

This transformation inserts an ExtractNode or a node derived from ExtractNode into the PSyIR of a schedule. At code creation time this node will use the PSyData API to create code that can write the input and output parameters to a file. The node might also create a stand-alone driver program that can read the created file and then execute the instrumented region. Examples are given in the derived classes DynamoExtractTrans and GOceanExtractTrans.

After applying the transformation the Nodes marked for extraction are children of the ExtractNode. Nodes to extract can be individual constructs within an Invoke (e.g. Loops containing a Kernel or BuiltIn call) or entire Invokes. This functionality does not support distributed memory.

Parameters:

node_class (psyclone.psyir.nodes.ExtractNode or derived class) – The Node class of which an instance will be inserted into the tree (defaults to ExtractNode), but can be any derived class.

Inheritance

Inheritance diagram of ExtractTrans
static determine_postfix(read_write_info, postfix='_post')

This function prevents any name clashes that can occur when adding the postfix to output variable names. For example, if there is an output variable ‘a’, the driver (and the output file) will contain two variables: ‘a’ and ‘a_post’. But if there is also another variable called ‘a_post’, a name clash would occur (two identical keys in the output file, and two identical local variables in the driver). In order to avoid this, the suffix ‘post’ is changed (to ‘post0’, ‘post1’, …) until any name clashes are avoided. This works for structured and non-structured types.

Parameters:
  • read_write_info (psyclone.psyir.tools.ReadWriteInfo) – information about all input and output parameters.

  • postfix (str) – the postfix to append to each output variable.

Returns:

a postfix that can be added to each output variable without generating a name clash.

Return type:

str

get_default_options()

Returns a new dictionary with additional options, specific to the transformation, that will be added to the user option. Any values specified by the user will take precedence. For the extract transformation, by default we want VariablesAccessInformation to report array arguments to lbound, ubound and size as read accesses, so we are certain these arrays will be included in the extraction.

Returns:

a dictionary with additional options.

Return type:

Dict[str, Any]

validate(node_list, options=None)

Performs validation checks specific to extract-based transformations.

Parameters:
  • node_list (list of psyclone.psyir.nodes.Node) – the list of Node(s) we are checking.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

Raises:
  • TransformationError – if distributed memory is configured.

  • TransformationError – if transformation is applied to a Kernel or a BuiltIn call without its parent Loop.

  • TransformationError – if transformation is applied to a Loop without its parent Directive when optimisations are applied.

  • TransformationError – if transformation is applied to an orphaned Directive without its parent Directive.

class psyclone.psyir.transformations.FoldConditionalReturnExpressionsTrans

Provides a transformation that folds conditional expressions with only a return statement inside so that the Return statement is moved to the end of the Routine and therefore it can be safely removed. This simplifies the control flow of the kernel to facilitate other transformations like kernel fusions. For example, the following code:

subroutine test(i)
    if (i < 5) then
        return
    endif
    if (i > 10) then
        return
    endif
    ! CODE
end subroutine

will be transformed to:

subroutine test(i)
    if (.not.(i < 5)) then
        if (.not.(i > 10)) then
            ! CODE
        endif
    endif
end subroutine

Inheritance

Inheritance diagram of FoldConditionalReturnExpressionsTrans
apply(node, options=None)

Apply this transformation to the supplied node.

Parameters:
  • node (psyclone.psyir.nodes.Routine) – the node to transform.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

property name

Returns the name of this transformation as a string.

validate(node, options=None)

Ensure that it is valid to apply this transformation to the supplied node.

Parameters:
  • node (psyclone.psyir.nodes.Routine) – the node to validate.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

Raises:

TransformationError – if the node is not a Routine.

class psyclone.psyir.transformations.HoistLocalArraysTrans

This transformation takes a Routine and promotes any local, ‘automatic’ arrays to Container scope:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.nodes import Assignment
>>> from psyclone.psyir.transformations import HoistLocalArraysTrans
>>> code = ("module test_mod\n"
...         "contains\n"
...         "  subroutine test_sub(n)\n"
...         "  integer :: i,j,n\n"
...         "  real :: a(n,n)\n"
...         "  real :: value = 1.0\n"
...         "  do i=1,n\n"
...         "    do j=1,n\n"
...         "      a(i,j) = value\n"
...         "    end do\n"
...         "  end do\n"
...         "  end subroutine test_sub\n"
...         "end module test_mod\n")
>>> psyir = FortranReader().psyir_from_source(code)
>>> hoist = HoistLocalArraysTrans()
>>> hoist.apply(psyir.walk(Routine)[0])
>>> print(FortranWriter()(psyir).lower())
module test_mod
  implicit none
  real, allocatable, dimension(:,:), private :: a
  public

  public :: test_sub

  contains
  subroutine test_sub(n)
    integer :: n
    integer :: i
    integer :: j
    real :: value = 1.0

    if (.not.allocated(a) .or. ubound(a, 1) /= n .or. ubound(a, 2) /= n) then
      if (allocated(a)) then
        deallocate(a)
      end if
      allocate(a(1 : n, 1 : n))
    end if
    do i = 1, n, 1
      do j = 1, n, 1
        a(i,j) = value
      enddo
    enddo

  end subroutine test_sub

end module test_mod

By default, the target routine will be rejected if it is found to contain an ACCRoutineDirective since this usually implies that the routine will be launched in parallel on the OpenACC device. This check can be disabled by setting ‘allow_accroutine’ to True in the options dictionary.

Inheritance

Inheritance diagram of HoistLocalArraysTrans
apply(node, options=None)

Applies the transformation to the supplied Routine node, moving any local arrays up to Container scope and adding a suitable allocation when they are first accessed. If there are no local arrays or the supplied Routine is a program then this method does nothing.

Parameters:
  • node (psyclone.psyir.nodes.Routine) – target PSyIR node.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

  • options["allow_accroutine"] (bool) – permit the target routine to contain an ACCRoutineDirective. These are forbidden by default because their presence usually indicates that the routine will be run in parallel on the OpenACC device.

validate(node, options=None)

Checks that the supplied node is a valid target for a hoist- local-arrays transformation. It must be a Routine that is within a Container (that is not a FileContainer).

Parameters:
  • node (subclass of psyclone.psyir.nodes.Routine) – target PSyIR node.

  • options (Optional[Dict[str, Any]]) – any options for the transformation.

Raises:
  • TransformationError – if the supplied node is not a Routine.

  • TransformationError – if the Routine is not within a Container (that is not a FileContainer).

  • TransformationError – if the routine contains an OpenACC routine directive and options[‘allow_accroutine’] is not True.

  • TransformationError – if any symbols corresponding to local arrays have a tag that already exists in the table of the parent Container.

class psyclone.psyir.transformations.HoistLoopBoundExprTrans

This transformation moves complex bounds expressions out of the loop construct and places them in integer scalar assignments before the loop.

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.nodes import Loop
>>> from psyclone.psyir.transformations import HoistTrans
>>> code = ("program test\n"
...         "  use mymod, only: mytype\n"
...         "  integer :: i,j,n\n"
...         "  real :: a(n)\n"
...         "  do i=mytype%start, UBOUND(a,1)\n"
...         "    a(i) = 1.0\n"
...         "  end do\n"
...         "end program\n")
>>> psyir = FortranReader().psyir_from_source(code)
>>> hoist = HoistLoopBoundExprTrans()
>>> hoist.apply(psyir.walk(Loop)[0])
>>> print(FortranWriter()(psyir))
program test
  use mymod, only : mytype
  integer :: i
  integer :: j
  integer :: n
  real, dimension(n) :: a
  integer :: loop_bound
  integer :: loop_bound_1

  loop_bound_1 = UBOUND(a, 1)
  loop_bound = mytype%start
  do i = loop_bound, loop_bound_1, 1
    a(i) = 1.0
  enddo

end program test

Inheritance

Inheritance diagram of HoistLoopBoundExprTrans
apply(node, options=None)

Move complex bounds expressions out of the given loop construct and place them in integer scalar assignments before the loop.

Parameters:
  • node (psyclone.psyir.nodes.Loop) – target PSyIR loop.

  • options (Dict[str, Any]) – a dictionary with options for transformations.

validate(node, options=None)

Checks that the supplied node is a valid target for the transformation.

Parameters:
  • node (psyclone.psyir.nodes.Loop) – target PSyIR loop.

  • options (Dict[str, Any]) – a dictionary with options for transformations.

Raises:
class psyclone.psyir.transformations.HoistTrans

This transformation takes an assignment and moves it outside of its parent loop if it is valid to do so. For example:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.nodes import Assignment
>>> from psyclone.psyir.transformations import HoistTrans
>>> code = ("program test\n"
...         "  integer :: i,j,n\n"
...         "  real :: a(n,n)\n"
...         "  real value\n"
...         "  do i=1,n\n"
...         "    value = 1.0\n"
...         "    do j=1,n\n"
...         "      a(i,j) = value\n"
...         "    end do\n"
...         "  end do\n"
...         "end program\n")
>>> psyir = FortranReader().psyir_from_source(code)
>>> hoist = HoistTrans()
>>> hoist.apply(psyir.walk(Assignment)[0])
>>> print(FortranWriter()(psyir))
program test
  integer :: i
  integer :: j
  integer :: n
  real, dimension(n,n) :: a
  real :: value

  value = 1.0
  do i = 1, n, 1
    do j = 1, n, 1
      a(i,j) = value
    enddo
  enddo

end program test

Inheritance

Inheritance diagram of HoistTrans
apply(node, options=None)

Applies the hoist transformation to the supplied assignment node within a loop, moving the assignment outside of the loop if it is valid to do so. Issue #1445 will also look to extend this transformation to other types of node.

Parameters:
  • node (subclass of psyclone.psyir.nodes.Assignment) – target PSyIR node.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

validate(node, options=None)

Checks that the supplied node is a valid target for a hoist transformation. At this stage only an assignment statement is allowed to be hoisted, see #1445. It should also be tested if there is a directive outside of the loop, see #1446

Parameters:
  • node (subclass of psyclone.psyir.nodes.Assignment) – target PSyIR node.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

Raises:
class psyclone.psyir.transformations.InlineTrans

This transformation takes a Call (which may have a return value) and replaces it with the body of the target routine. It is used as follows:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.nodes import Call, Routine
>>> from psyclone.psyir.transformations import InlineTrans
>>> code = """
... module test_mod
... contains
...   subroutine run_it()
...     integer :: i
...     real :: a(10)
...     do i=1,10
...       a(i) = 1.0
...       call sub(a(i))
...     end do
...   end subroutine run_it
...   subroutine sub(x)
...     real, intent(inout) :: x
...     x = 2.0*x
...   end subroutine sub
... end module test_mod"""
>>> psyir = FortranReader().psyir_from_source(code)
>>> call = psyir.walk(Call)[0]
>>> inline_trans = InlineTrans()
>>> inline_trans.apply(call)
>>> # Uncomment the following line to see a text view of the schedule
>>> # print(psyir.walk(Routine)[0].view())
>>> print(FortranWriter()(psyir.walk(Routine)[0]))
subroutine run_it()
  integer :: i
  real, dimension(10) :: a

  do i = 1, 10, 1
    a(i) = 1.0
    a(i) = 2.0 * a(i)
  enddo

end subroutine run_it

Warning

Routines/calls with any of the following characteristics are not supported and will result in a TransformationError:

  • the routine is not in the same file as the call;

  • the routine contains an early Return statement;

  • the routine contains a variable with UnknownInterface;

  • the routine contains a variable with StaticInterface;

  • the routine contains an UnsupportedType variable with ArgumentInterface;

  • the routine has a named argument;

  • the shape of any array arguments as declared inside the routine does not match the shape of the arrays being passed as arguments;

  • the routine accesses an un-resolved symbol;

  • the routine accesses a symbol declared in the Container to which it belongs.

Some of these restrictions will be lifted by #924.

Inheritance

Inheritance diagram of InlineTrans
apply(node, options=None)

Takes the body of the routine that is the target of the supplied call and replaces the call with it.

Parameters:
  • node (psyclone.psyir.nodes.Routine) – target PSyIR node.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

validate(node, options=None)

Checks that the supplied node is a valid target for inlining.

Parameters:
  • node (subclass of psyclone.psyir.nodes.Routine) – target PSyIR node.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

Raises:
class psyclone.psyir.transformations.Abs2CodeTrans

Provides a transformation from a PSyIR ABS Operator node to equivalent code in a PSyIR tree. Validity checks are also performed.

The transformation replaces

R = ABS(X)

with the following logic:

IF X < 0.0:
    R = X*-1.0
ELSE:
    R = X

Inheritance

Inheritance diagram of Abs2CodeTrans
apply(node, options=None)

Apply the ABS intrinsic conversion transformation to the specified node. This node must be an ABS UnaryOperation. The ABS UnaryOperation is converted to equivalent inline code. This is implemented as a PSyIR transform from:

R = ... ABS(X) ...

to:

tmp_abs = X
if tmp_abs < 0.0:
    res_abs = tmp_abs*-1.0
else:
    res_abs = tmp_abs
R = ... res_abs ...

where X could be an arbitrarily complex PSyIR expression and ... could be arbitrary PSyIR code.

This transformation requires the operation node to be a descendent of an assignment and will raise an exception if this is not the case.

Parameters:
class psyclone.psyir.transformations.DotProduct2CodeTrans

Provides a transformation from a PSyIR DOT_PRODUCT Operator node to equivalent code in a PSyIR tree. Validity checks are also performed.

If R is a scalar and A, and B have dimension N, The transformation replaces:

R = ... DOT_PRODUCT(A,B) ...

with the following code:

TMP = 0.0
do I=1,N
    TMP = TMP + A(i)*B(i)
R = ... TMP ...

For example:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.nodes import IntrinsicCall
>>> from psyclone.psyir.transformations import DotProduct2CodeTrans
>>> code = ("subroutine dot_product_test(v1,v2)\n"
...         "real,intent(in) :: v1(10), v2(10)\n"
...         "real :: result\n"
...         "result = dot_product(v1,v2)\n"
...         "end subroutine\n")
>>> psyir = FortranReader().psyir_from_source(code)
>>> trans = DotProduct2CodeTrans()
>>> trans.apply(psyir.walk(IntrinsicCall)[0])
>>> print(FortranWriter()(psyir))
subroutine dot_product_test(v1, v2)
  real, dimension(10), intent(in) :: v1
  real, dimension(10), intent(in) :: v2
  real :: result
  integer :: i
  real :: res_dot_product

  res_dot_product = 0.0
  do i = 1, 10, 1
    res_dot_product = res_dot_product + v1(i) * v2(i)
  enddo
  result = res_dot_product

end subroutine dot_product_test

Inheritance

Inheritance diagram of DotProduct2CodeTrans
apply(node, options=None)

Apply the DOT_PRODUCT intrinsic conversion transformation to the specified node. This node must be a DOT_PRODUCT BinaryOperation. If the transformation is successful then an assignment which includes a DOT_PRODUCT BinaryOperation node is converted to equivalent inline code.

Parameters:
validate(node, options=None)

Perform checks to ensure that it is valid to apply the DotProduct2CodeTran transformation to the supplied node.

Note, this validation does not check for invalid argument combinations to dot_product (e.g. different precision or different datatypes) as that should have already been picked up when creating the PSyIR.

Parameters:
Raises:
  • TransformationError – if one of the arguments is not a Reference node.

  • TransformationError – if an argument does not use array slice notation and is not a 1d array.

  • TransformationError – if an argument uses array slice notation but the array slice is not for the first dimension of the array.

  • TransformationError – if an argument uses array slice notation but it is not for the full range of the dimension.

class psyclone.psyir.transformations.Matmul2CodeTrans

Provides a transformation from a PSyIR MATMUL Operator node to equivalent code in a PSyIR tree. Validity checks are also performed.

For a matrix-vector multiplication, if the dimensions of R, A, and B are R(N), A(N,M), B(M), the transformation replaces:

R=MATMUL(A,B)

with the following code:

do i=1,N
    R(i) = 0.0
    do j=1,M
        R(i) = R(i) + A(i,j) * B(j)

For a matrix-matrix multiplication, if the dimensions of R, A, and B are R(P,M), A(P,N), B(N,M), the MATMUL is replaced with the following code:

do j=1,M
    do i=1,P
        R(i,j) = 0.0
        do ii=1,N
            R(i,j) = R(i,j) + A(i,ii) * B(ii,j)

Note that this transformation does not support the case where A is a rank-1 array.

Inheritance

Inheritance diagram of Matmul2CodeTrans
apply(node, options=None)

Apply the MATMUL intrinsic conversion transformation to the specified node. This node must be a MATMUL IntrinsicCall. The first argument must currently have two dimensions while the second must have either one or two dimensions. Each argument is permitted to have additional dimensions (i.e. more than 2) but in each case it is only the first one or two which may be ranges. Further, the ranges must currently be for the full index space for that dimension (i.e. array subsections are not supported). If the transformation is successful then an assignment which includes a MATMUL IntrinsicCall node is converted to equivalent inline code.

Parameters:
validate(node, options=None)

Perform checks to ensure that it is valid to apply the Matmul2CodeTran transformation to the supplied node.

Parameters:
Raises:
class psyclone.psyir.transformations.Max2CodeTrans

Provides a transformation from a PSyIR MAX Intrinsic node to equivalent code in a PSyIR tree. Validity checks are also performed (by a parent class).

The transformation replaces

R = MAX(A, B, C ...)

with the following logic:

R = A
if B > R:
    R = B
if C > R:
    R = C
...

Inheritance

Inheritance diagram of Max2CodeTrans
class psyclone.psyir.transformations.Min2CodeTrans

Provides a transformation from a PSyIR MIN Intrinsic node to equivalent code in a PSyIR tree. Validity checks are also performed (by a parent class).

The transformation replaces

R = MIN(A, B, C ...)

with the following logic:

R = A
if B < R:
    R = B
if C < R:
    R = C
...

Inheritance

Inheritance diagram of Min2CodeTrans
class psyclone.psyir.transformations.Sign2CodeTrans

Provides a transformation from a PSyIR SIGN intrinsic node to equivalent code in a PSyIR tree. Validity checks are also performed.

The transformation replaces

R = SIGN(A, B)

with the following logic:

R = ABS(A)
if B < 0.0:
    R = R*-1.0

i.e. the value of A with the sign of B

Inheritance

Inheritance diagram of Sign2CodeTrans
apply(node, options=None)

Apply the SIGN intrinsic conversion transformation to the specified node. This node must be a SIGN IntrinsicCall. The SIGN IntrinsicCall is converted to equivalent inline code. This is implemented as a PSyIR transform from:

R = ... SIGN(A, B) ...

to:

tmp_abs = A
if tmp_abs < 0.0:
    res_abs = tmp_abs*-1.0
else:
    res_abs = tmp_abs
res_sign = res_abs
tmp_sign = B
if tmp_sign < 0.0:
    res_sign = res_sign*-1.0
R = ... res_sign ...

where A and B could be arbitrarily complex PSyIR expressions, ... could be arbitrary PSyIR code and where ABS has been replaced with inline code by the NemoAbsTrans transformation.

This transformation requires the IntrinsicCall node to be a child of an assignment and will raise an exception if this is not the case.

Parameters:
class psyclone.psyir.transformations.Sum2LoopTrans

Provides a transformation from a PSyIR SUM IntrinsicCall node to an equivalent PSyIR loop structure that is suitable for running in parallel on CPUs and GPUs. Validity checks are also performed.

If SUM contains a single positional argument which is an array, all elements of that array are summed and the result returned in the scalar R.

R = SUM(ARRAY)

For example, if the array is two dimensional, the equivalent code for real data is:

R = 0.0
DO J=LBOUND(ARRAY,2),UBOUND(ARRAY,2)
  DO I=LBOUND(ARRAY,1),UBOUND(ARRAY,1)
    R = R + ARRAY(I,J)

If the mask argument is provided then the mask is used to determine whether the sum is applied:

R = SUM(ARRAY, mask=MOD(ARRAY, 2.0)==1)

If the array is two dimensional, the equivalent code for real data is:

R = 0.0
DO J=LBOUND(ARRAY,2),UBOUND(ARRAY,2)
  DO I=LBOUND(ARRAY,1),UBOUND(ARRAY,1)
    IF (MOD(ARRAY(I,J), 2.0)==1) THEN
      R = R + ARRAY(I,J)

The dimension argument is currently not supported and will result in a TransformationError exception being raised.

R = SUM(ARRAY, dimension=2)

The array passed to MAXVAL may use any combination of array syntax, array notation, array sections and scalar bounds:

R = SUM(ARRAY) ! array syntax
R = SUM(ARRAY(:,:)) ! array notation
R = SUM(ARRAY(1:10,lo:hi)) ! array sections
R = SUM(ARRAY(1:10,:)) ! mix of array section and array notation
R = SUM(ARRAY(1:10,2)) ! mix of array section and scalar bound

For example:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.transformations import Sum2LoopTrans
>>> code = ("subroutine sum_test(array,n,m)\n"
...         "  integer :: n, m\n"
...         "  real :: array(10,10)\n"
...         "  real :: result\n"
...         "  result = sum(array)\n"
...         "end subroutine\n")
>>> psyir = FortranReader().psyir_from_source(code)
>>> sum_node = psyir.children[0].children[0].children[1]
>>> Sum2LoopTrans().apply(sum_node)
>>> print(FortranWriter()(psyir))
subroutine sum_test(array, n, m)
  integer :: n
  integer :: m
  real, dimension(10,10) :: array
  real :: result
  integer :: idx
  integer :: idx_1

  result = 0.0
  do idx = 1, 10, 1
    do idx_1 = 1, 10, 1
      result = result + array(idx_1,idx)
    enddo
  enddo

end subroutine sum_test

Inheritance

Inheritance diagram of Sum2LoopTrans
class psyclone.psyir.transformations.LoopFuseTrans

Provides a generic loop-fuse transformation to two Nodes in the PSyIR of a Schedule after performing validity checks for the supplied Nodes. Examples are given in the descriptions of any children classes.

If loops have different named loop variables, when possible the loop variable of the second loop will be renamed to be the same as the first loop. This has the side effect that the second loop’s variable will no longer have its value modified, with the expectation that that value isn’t used anymore.

Note that the validation of this transformation still has several shortcomings, especially for domain API loops. Use at your own risk.

Inheritance

Inheritance diagram of LoopFuseTrans
apply(node1, node2, options=None)

Fuses two loops represented by psyclone.psyir.nodes.Node objects after performing validity checks.

If the two loops don’t have the same loop variable, the second loop’s variable (and references to it inside the loop) will be changed to be references to the first loop’s variable before merging. This has the side effect that the second loop’s variable will no longer have its value modified, with the expectation that that value isn’t used after.

Parameters:
validate(node1, node2, options=None)

Performs various checks to ensure that it is valid to apply the LoopFuseTrans transformation to the supplied Nodes.

Parameters:
  • node1 (psyclone.psyir.nodes.Node) – the first Node that is being checked.

  • node2 (psyclone.psyir.nodes.Node) – the second Node that is being checked.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

  • options["force"] (bool) – whether to force fusion of the target loop (i.e. ignore any dependence analysis). This only skips a limited number of the checks, and does not fully force merging.

Raises:
class psyclone.psyir.transformations.LoopSwapTrans

Provides a loop-swap transformation, e.g.:

DO j=1, m
    DO i=1, n

becomes:

DO i=1, n
    DO j=1, m

This transform is used as follows:

>>> from psyclone.parse.algorithm import parse
>>> from psyclone.psyGen import PSyFactory
>>> ast, invokeInfo = parse("shallow_alg.f90")
>>> psy = PSyFactory("gocean1.0").create(invokeInfo)
>>> schedule = psy.invokes.get('invoke_0').schedule
>>> # Uncomment the following line to see a text view of the schedule
>>> # print(schedule.view())
>>>
>>> from psyclone.transformations import LoopSwapTrans
>>> swap = LoopSwapTrans()
>>> swap.apply(schedule.children[0])
>>> # Uncomment the following line to see a text view of the schedule
>>> # print(schedule.view())

Inheritance

Inheritance diagram of LoopSwapTrans
apply(node, options=None)

The argument outer must be a loop which has exactly one inner loop. This transform then swaps the outer and inner loop.

Parameters:
  • outer (psyclone.psyir.nodes.Loop) – the node representing the outer loop.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

Raises:

TransformationError – if the supplied node does not allow a loop swap to be done.

validate(node, options=None)

Checks if the given node contains a valid Fortran structure to allow swapping loops. This means the node must represent a loop, and it must have exactly one child that is also a loop.

Parameters:
  • node_outer (py:class:psyclone.psyir.nodes.Loop) – a Loop node from an AST.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

Raises:
class psyclone.psyir.transformations.LoopTiling2DTrans

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:

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

Inheritance

Inheritance diagram of LoopTiling2DTrans
apply(node, options=None)

Converts the given 2D Loop construct into a tiled version of the nested loops.

Parameters:
  • node (psyclone.psyir.nodes.Loop) – the loop to transform.

  • options (Optional[Dict[str, Any]]) – a dict with options for transformations.

  • options["tilesize"] (int) – The size of the resulting tile, currently square tiles are always used. If not specified, the value 32 is used.

validate(node, options=None)

Validates that the given Loop node can have a LoopTiling2DTrans applied.

Parameters:
  • node (psyclone.psyir.nodes.Loop) – the loop to validate.

  • options (Optional[Dict[str, Any]]) – a dict with options for transformation.

  • options["tilesize"] (int) – The size of the resulting tile, currently square tiles are always used. If not specified, the value 32 is used.

Raises:
class psyclone.psyir.transformations.LoopTrans

This abstract class is a base class for all transformations that act on a Loop node. It gives access to a validate function that makes sure that the supplied node is a Loop. We also check that all nodes to be enclosed are valid for this transformation - this requires that the sub-class populate the excluded_node_types tuple.

Inheritance

Inheritance diagram of LoopTrans
property name
Returns:

the name of this class.

Return type:

str

validate(node, options=None)

Checks that the supplied node is a valid target for a loop transformation.

Parameters:
  • node (subclass of psyclone.psyir.nodes.Node) – target PSyIR node.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

  • options["node-type-check"] (bool) – this flag controls if the type of the nodes enclosed in the loop should be tested to avoid including unsupported nodes in a transformation.

Raises:
class psyclone.psyir.transformations.Maxval2LoopTrans

Provides a transformation from a PSyIR MAXVAL IntrinsicCall node to an equivalent PSyIR loop structure that is suitable for running in parallel on CPUs and GPUs. Validity checks are also performed.

If MAXVAL contains a single positional argument which is an array, the maximum value of all of the elements in the array is returned in the the scalar R.

R = MAXVAL(ARRAY)

For example, if the array is two dimensional, the equivalent code for real data is:

R = -HUGE(R)
DO J=LBOUND(ARRAY,2),UBOUND(ARRAY,2)
  DO I=LBOUND(ARRAY,1),UBOUND(ARRAY,1)
    R = MAX(R, ARRAY(I,J))

If the mask argument is provided then the mask is used to determine whether the maxval is applied:

R = MAXVAL(ARRAY, mask=MOD(ARRAY, 2.0)==1)

If the array is two dimensional, the equivalent code for real data is:

R = -HUGE(R)
DO J=LBOUND(ARRAY,2),UBOUND(ARRAY,2)
  DO I=LBOUND(ARRAY,1),UBOUND(ARRAY,1)
    IF (MOD(ARRAY(I,J), 2.0)==1) THEN
      R = MAX(R, ARRAY(I,J))

The dimension argument is currently not supported and will result in a TransformationError exception being raised.

R = MAXVAL(ARRAY, dimension=2)

The array passed to MAXVAL may use any combination of array syntax, array notation, array sections and scalar bounds:

R = MAXVAL(ARRAY) ! array syntax
R = MAXVAL(ARRAY(:,:)) ! array notation
R = MAXVAL(ARRAY(1:10,lo:hi)) ! array sections
R = MAXVAL(ARRAY(1:10,:)) ! mix of array section and array notation
R = MAXVAL(ARRAY(1:10,2)) ! mix of array section and scalar bound

An example use of this transformation is given below:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.transformations import Maxval2LoopTrans
>>> code = ("subroutine maxval_test(array)\n"
...         "  real :: array(10,10)\n"
...         "  real :: result\n"
...         "  result = maxval(array)\n"
...         "end subroutine\n")
>>> psyir = FortranReader().psyir_from_source(code)
>>> sum_node = psyir.children[0].children[0].children[1]
>>> Maxval2LoopTrans().apply(sum_node)
>>> print(FortranWriter()(psyir))
subroutine maxval_test(array)
  real, dimension(10,10) :: array
  real :: result
  integer :: idx
  integer :: idx_1

  result = -HUGE(result)
  do idx = 1, 10, 1
    do idx_1 = 1, 10, 1
      result = MAX(result, array(idx_1,idx))
    enddo
  enddo

end subroutine maxval_test

Inheritance

Inheritance diagram of Maxval2LoopTrans
class psyclone.psyir.transformations.Minval2LoopTrans

Provides a transformation from a PSyIR MINVAL IntrinsicCall node to an equivalent PSyIR loop structure that is suitable for running in parallel on CPUs and GPUs. Validity checks are also performed.

If MINVAL contains a single positional argument which is an array, the minimum value of all of the elements in the array is returned in the the scalar R.

R = MINVAL(ARRAY)

For example, if the array is two dimensional, the equivalent code for real data is:

R = HUGE(R)
DO J=LBOUND(ARRAY,2),UBOUND(ARRAY,2)
  DO I=LBOUND(ARRAY,1),UBOUND(ARRAY,1)
    R = MIN(R, ARRAY(I,J))

If the mask argument is provided then the mask is used to determine whether the minval is applied:

R = MINVAL(ARRAY, mask=MOD(ARRAY, 2.0)==1)

If the array is two dimensional, the equivalent code for real data is:

R = HUGE(R)
DO J=LBOUND(ARRAY,2),UBOUND(ARRAY,2)
  DO I=LBOUND(ARRAY,1),UBOUND(ARRAY,1)
    IF (MOD(ARRAY(I,J), 2.0)==1) THEN
      R = MIN(R, ARRAY(I,J))

The dimension argument is currently not supported and will result in a TransformationError exception being raised.

R = MINVAL(ARRAY, dimension=2)

The array passed to MINVAL may use any combination of array syntax, array notation, array sections and scalar bounds:

R = MINVAL(ARRAY) ! array syntax
R = MINVAL(ARRAY(:,:)) ! array notation
R = MINVAL(ARRAY(1:10,lo:hi)) ! array sections
R = MINVAL(ARRAY(1:10,:)) ! mix of array section and array notation
R = MINVAL(ARRAY(1:10,2)) ! mix of array section and scalar bound

For example:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.transformations import Minval2LoopTrans
>>> code = ("subroutine minval_test(array)\n"
...         "  real :: array(10,10)\n"
...         "  real :: result\n"
...         "  result = minval(array)\n"
...         "end subroutine\n")
>>> psyir = FortranReader().psyir_from_source(code)
>>> sum_node = psyir.children[0].children[0].children[1]
>>> Minval2LoopTrans().apply(sum_node)
>>> print(FortranWriter()(psyir))
subroutine minval_test(array)
  real, dimension(10,10) :: array
  real :: result
  integer :: idx
  integer :: idx_1

  result = HUGE(result)
  do idx = 1, 10, 1
    do idx_1 = 1, 10, 1
      result = MIN(result, array(idx_1,idx))
    enddo
  enddo

end subroutine minval_test

Inheritance

Inheritance diagram of Minval2LoopTrans
class psyclone.psyir.transformations.NanTestTrans(node_class=<class 'psyclone.psyir.nodes.nan_test_node.NanTestNode'>)

This transformation inserts a NanTestNode into the PSyIR of a schedule. At code creation time this node will use the PSyData API to create code that will verify all input parameters are not NANs and not infinite, and the same for all output parameters.

After applying the transformation the Nodes marked for verification are children of the NanTestNode. Nodes to verify can be individual constructs within an Invoke (e.g. Loops containing a Kernel or BuiltIn call) or entire Invokes.

Parameters:

node_class (psyclone.psyir.nodes.NanTestNode or derived class) – The class of Node which will be inserted into the tree (defaults to NanTestNode), but can be any derived class.

Inheritance

Inheritance diagram of NanTestTrans
validate(node_list, options=None)

Performs validation checks specific to nan-test transformations. This function is only here so that it is documented.

Parameters:
  • node_list (list of psyclone.psyir.nodes.Node) – the list of Node(s) we are checking.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

Raises:
  • TransformationError – if transformation is applied to a Kernel or a BuiltIn call without its parent Loop.

  • TransformationError – if transformation is applied to a Loop without its parent Directive when optimisations are applied.

  • TransformationError – if transformation is applied to an orphaned Directive without its parent Directive.

class psyclone.psyir.transformations.OMPLoopTrans(omp_directive='do', omp_schedule='auto')

Adds an OpenMP directive to parallelise this loop. It can insert different directives such as “omp do/for”, “omp parallel do/for”, “omp teams distribute parallel do/for” or “omp loop” depending on the provided parameters. The OpenMP schedule to use can also be specified, but this will be ignored in case of the “omp loop” (as the ‘schedule’ clause is not valid for this specific directive). The configuration-defined ‘reprod’ parameter also specifies whether a manual reproducible reproduction is to be used. Note, reproducible in this case means obtaining the same results with the same number of OpenMP threads, not for different numbers of OpenMP threads.

Parameters:
  • omp_schedule (str) – the OpenMP schedule to use. Defaults to ‘auto’.

  • omp_directive (str) – choose which OpenMP loop directive to use. Defaults to “omp do”

For example:

>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.nodes import Loop
>>> from psyclone.transformations import OMPLoopTrans, OMPParallelTrans
>>>
>>> psyir = FortranReader().psyir_from_source("""
...     subroutine my_subroutine()
...         integer, dimension(10, 10) :: A
...         integer :: i
...         integer :: j
...         do i = 1, 10
...             do j = 1, 10
...                 A(i, j) = 0
...             end do
...         end do
...     end subroutine
...     """)
>>> loop = psyir.walk(Loop)[0]
>>> omplooptrans1 = OMPLoopTrans(omp_schedule="dynamic",
...                              omp_directive="paralleldo")
>>> omplooptrans1.apply(loop)
>>> print(FortranWriter()(psyir))
subroutine my_subroutine()
  integer, dimension(10,10) :: a
  integer :: i
  integer :: j

  !$omp parallel do default(shared), private(i,j), schedule(dynamic)
  do i = 1, 10, 1
    do j = 1, 10, 1
      a(i,j) = 0
    enddo
  enddo
  !$omp end parallel do

end subroutine my_subroutine

Inheritance

Inheritance diagram of OMPLoopTrans
apply(node, options=None)

Apply the OMPLoopTrans transformation to the specified PSyIR Loop.

Parameters:
  • node (psyclone.psyir.nodes.Node) – the supplied node to which we will apply the OMPLoopTrans transformation

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations and validation.

  • options["reprod"] (bool) – indicating whether reproducible reductions should be used. By default the value from the config file will be used.

property omp_directive
Returns:

the type of OMP directive that this transformation will insert.

Return type:

str

property omp_schedule
Returns:

the OpenMP schedule that will be specified by this transformation.

Return type:

str

class psyclone.psyir.transformations.OMPTargetTrans

Adds an OpenMP target directive to a region of code.

For example:

>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.nodes import Loop
>>> from psyclone.psyir.transformations import OMPTargetTrans
>>>
>>> tree = FortranReader().psyir_from_source("""
...     subroutine my_subroutine()
...         integer, dimension(10, 10) :: A
...         integer :: i
...         integer :: j
...         do i = 1, 10
...             do j = 1, 10
...                 A(i, j) = 0
...             end do
...         end do
...     end subroutine
...     """)
>>> OMPTargetTrans().apply(tree.walk(Loop)[0])
>>> print(FortranWriter()(tree))
subroutine my_subroutine()
  integer, dimension(10,10) :: a
  integer :: i
  integer :: j

  !$omp target
  do i = 1, 10, 1
    do j = 1, 10, 1
      a(i,j) = 0
    enddo
  enddo
  !$omp end target

end subroutine my_subroutine

Inheritance

Inheritance diagram of OMPTargetTrans
apply(node, options=None)

Insert an OMPTargetDirective before the provided node or list of nodes.

Parameters:
  • node (List[psyclone.psyir.nodes.Node]) – the PSyIR node or nodes to enclose in the OpenMP target region.

  • options (Optional[Dict[str,Any]]) – a dictionary with options for transformations.

class psyclone.psyir.transformations.OMPTaskTrans

Apply an OpenMP Task Transformation to a Loop. The Loop must be within an OpenMP Serial region (Single or Master) at codegen time. Once lowering begins, no more modifications to the tree should occur as the task directives do not recompute dependencies after lowering. In the future it may be possible to do this through an _update_node implementation.

Inheritance

Inheritance diagram of OMPTaskTrans
apply(node, options=None)

Apply the OMPTaskTrans to the specified node in a Schedule.

Can only be applied to a Loop.

The specified node is wrapped by directives during code generation like so:

!$OMP TASK
...
!$OMP END TASK

At code-generation time, this node must be within (i.e. a child of) an OpenMP Serial region (OpenMP Single or OpenMP Master)

Any kernels or Calls will be inlined into the region before the task transformation is applied.

Parameters:
  • node (psyclone.psyir.nodes.Loop) – the supplied node to which we will apply the OMPTaskTrans transformation

  • options (dictionary of string:values or None) – a dictionary with options for transformations and validation.

property name
Returns:

the name of this transformation.

Return type:

str

validate(node, options=None)

Validity checks for input arguments.

Parameters:
  • node (psyclone.psyir.nodes.Loop) – the Loop node to validate.

  • options (dict of string:values or None) – a dictionary with options for transformations.

class psyclone.psyir.transformations.OMPTaskwaitTrans

Adds zero or more OpenMP Taskwait directives to an OMP parallel region. This transformation will add directives to satisfy dependencies between Taskloop directives without an associated taskgroup (i.e. no nogroup clause). It also tries to minimise the number added to maximise available parallelism.

For example:

>>> from pysclone.parse.algorithm import parse
>>> from psyclone.psyGen import PSyFactory
>>> api = "gocean1.0"
>>> filename = "nemolite2d_alg.f90"
>>> ast, invokeInfo = parse(filename, api=api, invoke_name="invoke")
>>> psy = PSyFactory(api).create(invokeInfo)
>>>
>>> from psyclone.transformations import OMPParallelTrans, OMPSingleTrans
>>> from psyclone.transformations import OMPTaskloopTrans
>>> from psyclone.psyir.transformations import OMPTaskwaitTrans
>>> singletrans = OMPSingleTrans()
>>> paralleltrans = OMPParallelTrans()
>>> tasklooptrans = OMPTaskloopTrans()
>>> taskwaittrans = OMPTaskwaitTrans()
>>>
>>> schedule = psy.invokes.get('invoke_0').schedule
>>> print(schedule.view())
>>>
>>> # Apply the OpenMP Taskloop transformation to *every* loop
>>> # in the schedule.
>>> # This ignores loop dependencies. These are handled by the
>>> # taskwait transformation.
>>> for child in schedule.children:
>>>     tasklooptrans.apply(child, nogroup = true)
>>> # Enclose all of these loops within a single OpenMP
>>> # SINGLE region
>>> singletrans.apply(schedule.children)
>>> # Enclose all of these loops within a single OpenMP
>>> # PARALLEL region
>>> paralleltrans.apply(schedule.children)
>>> taskwaittrans.apply(schedule.children)
>>> print(schedule.view())

Inheritance

Inheritance diagram of OMPTaskwaitTrans
apply(node, options=None)

Apply an OMPTaskwait Transformation to the supplied node (which must be an OMPParallelDirective). In the generated code this corresponds to adding zero or more OMPTaskwaitDirectives as appropriate:

!$OMP PARALLEL
  ...
  !$OMP TASKWAIT
  ...
  !$OMP TASKWAIT
  ...
!$OMP END PARALLEL
Parameters:
  • node (psyclone.psyir.nodes.OMPParallelDirective) – the node to which to apply the transformation.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations and validation.

  • options["fail_on_no_taskloop"] (bool) – indicating whether this should throw an error if no OMPTaskloop nodes are found in this tree. This can be safely disabled as if there are no Taskloop nodes the result of this transformation is valid OpenMP code. Default is True

static get_forward_dependence(taskloop, root)

Returns the next forward dependence for a taskloop using the dependence-analysis functionality provided by psyclone.psyir.tools.dependency_tools. Forward dependencies can be of the following types: Loop OMPDoDirective OMPTaskloopDirective OMPTaskwaitDirective (If in same OMPSingle and that single has nowait=False) OMPSingleDirective (If ancestor OMPSingle has nowait=False)

Loop, OMPDoDirective, OMPTaskloopDirective types are returned when a following directive is found which has a RaW, WaR or WaW dependency to taskloop.

An OMPTaskwaitDirective type is returned when a following directive is found inside the same parent OMPSingleDirective which has no nowait clause applied.

An OMPSingleDirective type is returned when the first dependency is within a different OMPSerialDirective, and the ancestor of taskloop is an OMPSingleDirective with no nowait clause.

The forward dependency is never a child of taskloop, and must have abs_position > taskloop.abs_position

Parameters:
Returns:

the forward_dependence of taskloop.

Return type:

psyclone.f2pygen.Node

validate(node, options=None)

Validity checks for input arguments.

Parameters:
  • node (psyclone.psyir.nodes.OMPParallelDirective) – the OMPParallelDirective node to validate.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

  • options["fail_on_no_taskloop"] (bool) – indicating whether this should throw an error if no OMPTaskloop nodes are found in this tree. This can be safely disabled as if there are no Taskloop nodes the result of this transformation is valid OpenMP code. Default is True.

Raises:
  • TransformationError – If the supplied node is not an OMPParallelDirective

  • TransformationError – If there are no OMPTaskloopDirective nodes in this tree.

  • TransformationError – If taskloop dependencies can’t be satisfied due to dependencies across barrierless OpenMP Serial Regions.

class psyclone.psyir.transformations.ParallelLoopTrans

Adds an abstract directive (it needs to be specified by sub-classing this transformation) to a loop indicating that it should be parallelised. It performs some data dependency checks to guarantee that the loop can be parallelised without changing the semantics of it.

Inheritance

Inheritance diagram of ParallelLoopTrans
apply(node, options=None)

Apply the Loop transformation to the specified node in a Schedule. This node must be a Loop since this transformation corresponds to wrapping the generated code with directives, e.g. for OpenMP:

!$OMP DO
do ...
   ...
end do
!$OMP END DO

At code-generation time (when gen_code()` is called), this node must be within (i.e. a child of) a PARALLEL region.

Parameters:
  • node (psyclone.psyir.nodes.Node) – the supplied node to which we will apply the Loop transformation.

  • options – a dictionary with options for transformations. :type options: Optional[Dict[str, Any]]

  • options["collapse"] (int) – the number of loops to collapse into single iteration space or None.

validate(node, options=None)

Perform validation checks before applying the transformation

Parameters:
  • node (psyclone.psyir.nodes.Node) – the node we are checking.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations. This transform supports “collapse”, which is the number of nested loops to collapse.

  • options["collapse"] (int) – number of nested loops to collapse or None.

  • options["force"] (bool) – whether to force parallelisation of the target loop (i.e. ignore any dependence analysis).

  • options["sequential"] (bool) – whether this is a sequential loop.

Raises:
class psyclone.psyir.transformations.Product2LoopTrans

Provides a transformation from a PSyIR PRODUCT IntrinsicCall node to an equivalent PSyIR loop structure that is suitable for running in parallel on CPUs and GPUs. Validity checks are also performed.

If PRODUCT contains a single positional argument which is an array, the maximum value of all of the elements in the array is returned in the the scalar R.

R = PRODUCT(ARRAY)

For example, if the array is two dimensional, the equivalent code for real data is:

R = 1.0
DO J=LBOUND(ARRAY,2),UBOUND(ARRAY,2)
  DO I=LBOUND(ARRAY,1),UBOUND(ARRAY,1)
    R = R * ARRAY(I,J)

If the mask argument is provided then the mask is used to determine whether the product is applied:

R = PRODUCT(ARRAY, mask=MOD(ARRAY, 2.0)==1)

If the array is two dimensional, the equivalent code for real data is:

R = 1.0
DO J=LBOUND(ARRAY,2),UBOUND(ARRAY,2)
  DO I=LBOUND(ARRAY,1),UBOUND(ARRAY,1)
    IF (MOD(ARRAY(I,J), 2.0)==1) THEN
      R = R * ARRAY(I,J)

The dimension argument is currently not supported and will result in a TransformationError exception being raised.

R = PRODUCT(ARRAY, dimension=2)

The array passed to PRODUCT may use any combination of array syntax, array notation, array sections and scalar bounds:

R = PRODUCT(ARRAY) ! array syntax
R = PRODUCT(ARRAY(:,:)) ! array notation
R = PRODUCT(ARRAY(1:10,lo:hi)) ! array sections
R = PRODUCT(ARRAY(1:10,:)) ! mix of array section and array notation
R = PRODUCT(ARRAY(1:10,2)) ! mix of array section and scalar bound

An example use of this transformation is given below:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.transformations import Product2LoopTrans
>>> code = ("subroutine product_test(array)\n"
...         "  real :: array(10,10)\n"
...         "  real :: result\n"
...         "  result = product(array)\n"
...         "end subroutine\n")
>>> psyir = FortranReader().psyir_from_source(code)
>>> product_node = psyir.children[0].children[0].children[1]
>>> Product2LoopTrans().apply(product_node)
>>> print(FortranWriter()(psyir))
subroutine product_test(array)
  real, dimension(10,10) :: array
  real :: result
  integer :: idx
  integer :: idx_1

  result = 1.0
  do idx = 1, 10, 1
    do idx_1 = 1, 10, 1
      result = result * array(idx_1,idx)
    enddo
  enddo

end subroutine product_test

Inheritance

Inheritance diagram of Product2LoopTrans
class psyclone.psyir.transformations.ProfileTrans

Create a profile region around a list of statements. For example:

>>> from psyclone.parse.algorithm import parse
>>> from psyclone.parse.utils import ParseError
>>> from psyclone.psyGen import PSyFactory, GenerationError
>>> from psyclone.psyir.transformations import ProfileTrans
>>> api = "gocean1.0"
>>> filename = "nemolite2d_alg.f90"
>>> ast, invokeInfo = parse(filename, api=api, invoke_name="invoke")
>>> psy = PSyFactory(api).create(invokeInfo)
>>>
>>> p_trans = ProfileTrans()
>>>
>>> schedule = psy.invokes.get('invoke_0').schedule
>>> print(schedule.view())
>>>
>>> # Enclose all children within a single profile region
>>> p_trans.apply(schedule.children)
>>> print(schedule.view())

This implementation relies completely on the base class PSyDataTrans for the actual work, it only adjusts the name etc, and the list of valid nodes.

Inheritance

Inheritance diagram of ProfileTrans
class psyclone.psyir.transformations.PSyDataTrans(node_class=<class 'psyclone.psyir.nodes.psy_data_node.PSyDataNode'>)

Create a PSyData region around a list of statements. For example:

>>> from psyclone.parse.algorithm import parse
>>> from psyclone.parse.utils import ParseError
>>> from psyclone.psyGen import PSyFactory
>>> api = "gocean1.0"
>>> ast, invoke_info = parse(SOURCE_FILE, api=api)
>>> psy = PSyFactory(api).create(invoke_info)
>>>
>>> from psyclone.psyir.transformations import PSyDataTrans
>>> data_trans = PSyDataTrans()
>>>
>>> schedule = psy.invokes.get('invoke_0').schedule
>>> # Uncomment the following line to see a text view of the schedule
>>> # print(schedule.view())
>>>
>>> # Enclose all children within a single PSyData region
>>> data_trans.apply(schedule.children)
>>> # Uncomment the following line to see a text view of the schedule
>>> # print(schedule.view())
>>> # Or to use custom region name:
>>> data_trans.apply(schedule.children,
...                  {"region_name": ("module","region")})
Parameters:

node_class (psyclone.psyir.nodes.ExtractNode) – The Node class of which an instance will be inserted into the tree (defaults to PSyDataNode).

Inheritance

Inheritance diagram of PSyDataTrans
apply(nodes, options=None)

Apply this transformation to a subset of the nodes within a schedule - i.e. enclose the specified Nodes in the schedule within a single PSyData region.

Parameters:
  • nodes (psyclone.psyir.nodes.Node or list of psyclone.psyir.nodes.Node) – can be a single node or a list of nodes.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

  • options["prefix"] (str) – a prefix to use for the PSyData module name (PREFIX_psy_data_mod) and the PSyDataType (PREFIX_PSYDATATYPE) - a “_” will be added automatically. It defaults to “”.

  • options["region_name"] ((str,str)) – an optional name to use for this PSyData area, provided as a 2-tuple containing a location name followed by a local name. The pair of strings should uniquely identify a region unless aggregate information is required (and is supported by the runtime library).

get_default_options()

Returns a new dictionary with additional options, specific to the transformation, that will be added to the user option. Any values specified by the user will take precedence.

Returns:

a dictionary with additional options.

Return type:

Dict[str, Any]

get_unique_region_name(nodes, options)

This function returns the region and module name. If they are specified in the user options, these names will just be returned (it is then up to the user to guarantee uniqueness). Otherwise a name based on the module and invoke will be created using indices to make sure the name is unique.

Parameters:
  • nodes (list of psyclone.psyir.nodes.Node) – a list of nodes.

  • options (Dict[str, Any]) – a dictionary with options for transformations.

  • options["region_name"] ((str,str)) – an optional name to use for this PSyData area, provided as a 2-tuple containing a location name followed by a local name. The pair of strings should uniquely identify a region unless aggregate information is required (and is supported by the runtime library).

merge_in_default_options(options)

This function returns a new dictionary which contains the default options for this transformation plus al user-specified options. Any user-specified option will take precedence over the default values.

Parameters:

options (Dict[str, Any]) – a dictionary with options for transformations.

Returns:

a new dictionary which merges the default options with the user-specified options.

Return type:

Dict[str:Any]

property name

This function returns the name of the transformation. It uses the Python 2/3 compatible way of returning the class name as a string, which means that the same function can be used for all derived classes.

Returns:

the name of this transformation as a string.

Return type:

str

validate(nodes, options=None)

Checks that the supplied list of nodes is valid, that the location for this node is valid (not between a loop-directive and its loop), that there aren’t any name clashes with symbols that must be imported from the appropriate PSyData library and finally, calls the validate method of the base class.

Parameters:
  • nodes ((list of) psyclone.psyir.nodes.Loop) – a node or list of nodes to be instrumented with PSyData API calls.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

  • options["prefix"] (str) – a prefix to use for the PSyData module name (PREFIX_psy_data_mod) and the PSyDataType (PREFIX_PSYDATATYPE) - a “_” will be added automatically. It defaults to “”.

  • options["region_name"] ((str,str)) – an optional name to use for this PSyData area, provided as a 2-tuple containing a location name followed by a local name. The pair of strings should uniquely identify a region unless aggregate information is required (and is supported by the runtime library).

Raises:
  • TransformationError – if the supplied list of nodes is empty.

  • TransformationError – if the PSyData node is inserted between an OpenMP/ACC directive and the loop(s) to which it applies.

  • TransformationError – if the ‘prefix’ or ‘region_name’ options are not valid.

  • TransformationError – if there will be a name clash between any existing symbols and those that must be imported from the appropriate PSyData library.

class psyclone.psyir.transformations.ReadOnlyVerifyTrans(node_class=<class 'psyclone.psyir.nodes.read_only_verify_node.ReadOnlyVerifyNode'>)

This transformation inserts a ReadOnlyVerifyNode or a node derived from ReadOnlyVerifyNode into the PSyIR of a schedule. At code creation time this node will use the PSyData API to create code that will verify that read-only quantities are not modified.

After applying the transformation the Nodes marked for verification are children of the ReadOnlyVerifyNode. Nodes to verify can be individual constructs within an Invoke (e.g. Loops containing a Kernel or BuiltIn call) or entire Invokes.

Parameters:

node_class (psyclone.psyir.nodes.ReadOnlyVerifyNode or derived class) – The class of Node which will be inserted into the tree (defaults to ReadOnlyVerifyNode), but can be any derived class.

Inheritance

Inheritance diagram of ReadOnlyVerifyTrans
get_default_options()

Returns a new dictionary with additional options, specific to the transformation, that will be added to the user option. Any values specified by the user will take precedence. For the read-only verify transformation, by default we want VariablesAccessInformation to report array arguments to lbound, ubound and size as read accesses, since these arguments should also not be overwritten.

Returns:

a dictionary with additional options.

Return type:

Dict[str, Any]

validate(node_list, options=None)

Performs validation checks specific to read-only-based transformations.

Parameters:
  • node_list (list of psyclone.psyir.nodes.Node) – the list of Node(s) we are checking.

  • options (Optional[Dict[str, Any]]) – a dictionary with options for transformations.

Raises:
  • TransformationError – if transformation is applied to a Kernel or a BuiltIn call without its parent Loop.

  • TransformationError – if transformation is applied to a Loop without its parent Directive when optimisations are applied.

  • TransformationError – if transformation is applied to an orphaned Directive without its parent Directive.

class psyclone.psyir.transformations.Reference2ArrayRangeTrans

Provides a transformation from PSyIR Array Notation (a reference to an Array) to a PSyIR Range. For example:

>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.nodes import Reference
>>> from psyclone.psyir.transformations import TransformationError
>>> CODE = ("program example\n"
...         "real :: a(:)\n"
...         "a = 0.0\n"
...         "end program\n")
>>> trans = Reference2ArrayRangeTrans()
>>> psyir = FortranReader().psyir_from_source(CODE)
>>> for reference in psyir.walk(Reference):
...    try:
...        trans.apply(reference)
...    except TransformationError:
...        pass
>>> print(FortranWriter()(psyir))
program example
  real, dimension(:) :: a

  a(:) = 0.0

end program example

This transformation does not currently support arrays within structures, see issue #1858.

Inheritance

Inheritance diagram of Reference2ArrayRangeTrans
apply(node, options=None)

Apply the Reference2ArrayRangeTrans transformation to the specified node. The node must be a Reference to an array. The Reference is replaced by an ArrayReference with appropriate explicit range nodes (termed colon notation in Fortran).

Parameters:
validate(node, options=None)

Check that the node is a Reference node and that the symbol it references is an array.

Parameters:
Raises:
  • TransformationError – if the node is not a Reference node or the Reference node not does not reference an array symbol.

  • TransformationError – if the Reference node is within an inquiry or DEALLOCATE intrinsic.

class psyclone.psyir.transformations.RegionTrans

This abstract class is a base class for all transformations that act on a list of nodes. It gives access to a validate function that makes sure that the nodes in the list are in the same order as in the original AST, no node is duplicated, and that all nodes have the same parent. We also check that all nodes to be enclosed are valid for this transformation - this requires that the sub-class populate the excluded_node_types tuple.

Inheritance

Inheritance diagram of RegionTrans
get_node_list(nodes)

This is a helper function for region based transformations. The parameter for any of those transformations is either a single node, a schedule, or a list of nodes. This function converts this into a list of nodes according to the parameter type. This function will always return a copy, to avoid issues e.g. if a child list of a node should be provided, and a transformation changes the order in this list (which would then also change the order of the nodes in the tree).

Parameters:
Returns:

a list of nodes.

Return type:

List[psyclone.psyir.nodes.Node]

Raises:

TransformationError – if the supplied parameter is neither a single Node, nor a Schedule, nor a list of Nodes.

validate(nodes, options=None)

Checks that the nodes in node_list are valid for a region transformation.

Parameters:
  • nodes (Union[psyclone.psyir.nodes.Node, psyclone.psyir.nodes.Schedule, List[psyclone.psyir.nodes.Node]) – can be a single node, a schedule or a list of nodes.

  • options (Optional[Dict[str,Any]]) – a dictionary with options for transformations.

  • options["node-type-check"] (bool) – this flag controls if the type of the nodes enclosed in the region should be tested to avoid using unsupported nodes inside a region.

Raises:
  • TransformationError – if the nodes in the list are not in the original order in which they are in the AST, a node is duplicated or the nodes have different parents.

  • TransformationError – if any of the nodes to be enclosed in the region are of an unsupported type.

  • TransformationError – if the parent of the supplied Nodes is not a Schedule or a Directive.

  • TransformationError – if the nodes are in a NEMO Schedule and the transformation acts on the child of a single-line If or Where statment.

  • TransformationError – if the supplied options are not a dictionary.

class psyclone.psyir.transformations.ReplaceInductionVariablesTrans

Move all supported induction variables out of the loop, and replace their usage inside the loop. For example:

>>> from psyclone.psyir.frontend.fortran import FortranReader
>>> from psyclone.psyir.nodes import Loop
>>> from psyclone.psyir.transformations import             ReplaceInductionVariablesTrans
>>> from psyclone.psyir.backend.fortran import FortranWriter
>>> psyir = FortranReader().psyir_from_source("""
... subroutine sub()
...     integer :: i, im, ic, tmp(100)
...     do i=1, 100
...         im = i - 1
...         ic = 2
...         tmp(i) = ic * im
...     enddo
... end subroutine sub""")
>>> loop = psyir.walk(Loop)[0]
>>> ReplaceInductionVariablesTrans().apply(loop)
>>> print(FortranWriter()(psyir))
subroutine sub()
  integer :: i
  integer :: im
  integer :: ic
  integer, dimension(100) :: tmp

  do i = 1, 100, 1
    tmp(i) = 2 * (i - 1)
  enddo
  ic = 2
  im = i - 1 - 1

end subroutine sub

The replaced induction variables assignments are added after the loop, so these variables will have the correct value if they are used elsewhere.

The following restrictions apply for the assignment to an induction variable:

  • the variable must be a scalar (i.e. no array access at all, not even a constant like a(3) or a%b(3)%c)

  • none of variables on the right-hand side can be written in the loop body (the loop variable is written in the Loop statement, not in the body, so it can be used).

  • Only intrinsic function calls are allowed on the RHS (since they are known to be elemental)

  • the assigned variable must not be read before the assignment.

  • the assigned variable cannot occur on the right-hand side (e.g. k = k + 3).

  • there must be only one assignment to this induction variable.

Inheritance

Inheritance diagram of ReplaceInductionVariablesTrans
apply(node, options=None)

Apply the ReplaceInductionVariablesTrans transformation to the specified node. The node must be a loop. In case of nested loops, the transformation might need to be applied several times, from the inner-most loop outwards.

Parameters:

node (psyclone.psyir.nodes.Loop) – a Loop node.

validate(node, options=None)

Perform various checks to ensure that it is valid to apply the ReplaceInductionVariablesTrans transformation to the supplied PSyIR Node.

Parameters:

node (psyclone.psyir.nodes.Assignment) – the node that is being checked.

Raises:

TransformationError – if the node argument is not a Loop.

Exceptions

exception psyclone.psyir.transformations.TransformationError(value)

Provides a PSyclone-specific error class for errors found during code transformation operations.

Inheritance

Inheritance diagram of TransformationError