Exemplo n.º 1
0
    def gen_code(self, parent):
        # pylint: disable=arguments-differ
        '''
        Generates the code required for NAN/infinite verification of the
        parameters of one or more Nodes. It uses the PSyData API (via
        the base class PSyDataNode) to create the required callbacks
        that will allow a library to do checks on parameters.

        :param parent: the parent of this Node in the PSyIR.
        :type parent: :py:class:`psyclone.psyir.nodes.Node`.

        '''

        # pylint: disable=import-outside-toplevel
        # This cannot be moved to the top, it would cause a circular import
        from psyclone.psyir.tools.dependency_tools import DependencyTools
        # Determine the variables to check:
        dep = DependencyTools()
        input_list, output_list = dep.get_in_out_parameters(self)

        options = {'pre_var_list': input_list, 'post_var_list': output_list}

        parent.add(CommentGen(parent, ""))
        parent.add(CommentGen(parent, " NanTestStart"))
        parent.add(CommentGen(parent, ""))
        super(NanTestNode, self).gen_code(parent, options)
        parent.add(CommentGen(parent, ""))
        parent.add(CommentGen(parent, " NanTestEnd"))
        parent.add(CommentGen(parent, ""))
Exemplo n.º 2
0
def test_inout_parameters_nemo(parser):
    '''Test detection of input and output parameters in NEMO.
    '''
    reader = FortranStringReader('''program test
                         integer :: ji, jj, jpi, jpj
                         real :: a(5,5), c(5,5), b
                         do jj = 1, jpj   ! loop 0
                            do ji = 1, jpi
                               a(ji, jj) = b+c(ji, jj)
                             end do
                         end do
                         end program test''')
    prog = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(prog)
    loops = psy.invokes.get("test").schedule

    dep_tools = DependencyTools()
    input_list = dep_tools.get_input_parameters(loops)
    # Use set to be order independent
    input_set = set(input_list)
    assert input_set == set(["b", "c", "jpi", "jpj"])

    output_list = dep_tools.get_output_parameters(loops)
    # Use set to be order independent
    output_set = set(output_list)
    assert output_set == set(["jj", "ji", "a"])

    in_list1, out_list1 = dep_tools.get_in_out_parameters(loops)

    assert in_list1 == input_list
    assert out_list1 == output_list
Exemplo n.º 3
0
def test_arrays_parallelise(parser):
    '''Tests the array checks of can_loop_be_parallelised.
    '''
    reader = FortranStringReader('''program test
                                 integer ji, jj, jk
                                 integer, parameter :: jpi=5, jpj=10
                                 real, dimension(jpi,jpi) :: mask, umask
                                 do jj = 1, jpj   ! loop 0
                                    do ji = 1, jpi
                                       mask(jk, jk) = -1.0d0
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 1
                                    do ji = 1, jpi
                                       mask(ji, jj) = umask(jk, jk)
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 2
                                    do ji = 1, jpi
                                       mask(jj, jj) = umask(ji, jj)
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 3
                                    do ji = 1, jpi
                                       mask(ji, jj) = mask(ji, jj+1)
                                     end do
                                 end do
                                 end program test''')
    prog = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(prog)
    loops = psy.invokes.get("test").schedule
    dep_tools = DependencyTools(["levels", "lat"])

    # Write to array that does not depend on parallel loop variable
    # Test that right default variable name (outer loop jj) is used.
    parallel = dep_tools.can_loop_be_parallelised(loops[0])
    assert not parallel
    assert "Variable 'mask' is written to, and does not depend on the loop "\
           "variable 'jj'" in dep_tools.get_all_messages()[0]

    # Write to array that does not depend on parallel loop variable
    parallel = dep_tools.can_loop_be_parallelised(loops[1], "jj")
    assert parallel
    assert not dep_tools.get_all_messages()

    # Use parallel loop variable in more than one dimension
    parallel = dep_tools.can_loop_be_parallelised(loops[2], "jj")
    assert not parallel
    assert "Variable 'mask' is using loop variable jj in index 0 and 1" in \
        dep_tools.get_all_messages()[0]

    # Use a stencil access (with write), which prevents parallelisation
    parallel = dep_tools.can_loop_be_parallelised(loops[3], "jj")
    assert not parallel
    assert "Variable mask is written and is accessed using indices jj + 1 "\
           "and jj and can therefore not be parallelised" \
           in dep_tools.get_all_messages()[0]
Exemplo n.º 4
0
def test_loop_parallelise_errors():
    '''Tests errors that should be raised from the can_loop_be_parallelised
    function.'''

    dep_tools = DependencyTools()
    with pytest.raises(TypeError) as err:
        # The loop object must be a Loop, not e.g. an int:
        loop = 1
        dep_tools.can_loop_be_parallelised(loop, "i")
    assert "node must be an instance of class Loop but got" in str(err)
Exemplo n.º 5
0
def test_const_argument():
    '''Check that using a const scalar as parameter works, i.e. is not
    listed as input variable.'''
    _, invoke = get_invoke("test00.1_invoke_kernel_using_const_scalar.f90",
                           api="gocean1.0",
                           idx=0)
    dep_tools = DependencyTools()
    input_list = dep_tools.get_input_parameters(invoke.schedule)
    # Make sure the constant '0' is not listed
    assert input_list == [
        'p_fld', 'p_fld%grid%subdomain%internal%xstop', 'p_fld%grid%tmask'
    ]
Exemplo n.º 6
0
def test_derived_type(parser):
    ''' Tests assignment to derived type variables. '''
    reader = FortranStringReader('''program test
                                 use my_mod, only: my_type
                                 type(my_type) :: a, b
                                 integer :: ji, jj, jpi, jpj
                                 do jj = 1, jpj   ! loop 0
                                    do ji = 1, jpi
                                       a%b(ji, jj) = a%b(ji, jj-1)+1
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 0
                                    do ji = 1, jpi
                                       a%b(ji, jj) = a%b(ji, jj-1)+1
                                       b%b(ji, jj) = b%b(ji, jj-1)+1
                                     end do
                                 end do
                                 end program test''')
    prog = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(prog)
    loops = psy.invokes.get("test").schedule
    dep_tools = DependencyTools(["levels", "lat"])

    parallel = dep_tools.can_loop_be_parallelised(loops[0], "jj")
    assert not parallel

    # Test that testing is stopped with the first unparallelisable statement
    parallel = dep_tools.can_loop_be_parallelised(loops[1], "jj")
    assert not parallel
    # Test that only one message is stored, i.e. no message for the
    # next assignment to a derived type.
    assert len(dep_tools.get_all_messages()) == 1

    parallel = dep_tools.can_loop_be_parallelised(loops[1],
                                                  "jj",
                                                  test_all_variables=True)
    assert not parallel
    # Now we must have two messages, one for each of the two assignments
    assert len(dep_tools.get_all_messages()) == 2

    # Test that variables are ignored as expected.
    parallel = dep_tools.\
        can_loop_be_parallelised(loops[1], "jj",
                                 signatures_to_ignore=[Signature(("a", "b"))])
    assert not parallel
    assert len(dep_tools.get_all_messages()) == 1

    # If both derived types are ignored, the loop should be marked
    # to be parallelisable
    parallel = dep_tools.\
        can_loop_be_parallelised(loops[1], "jj",
                                 signatures_to_ignore=[Signature(("a", "b")),
                                                       Signature(("b", "b"))])
    assert len(dep_tools.get_all_messages()) == 0
    assert parallel
Exemplo n.º 7
0
def test_loop_type(parser):
    '''Tests general functionality of can_loop_be_parallelised.
    '''
    reader = FortranStringReader('''program test
                                 do ji = 1, jpi
                                   xmask(ji,1,1) = -1.0d0
                                 end do
                                 end program test''')
    prog = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(prog)
    loop = psy.invokes.get("test").schedule[0]
    dep_tools = DependencyTools(["levels", "lat"])

    # Check a loop that has the wrong loop type
    parallel = dep_tools.can_loop_be_parallelised(loop, "ji", False)
    assert not parallel
    assert "wrong loop type 'lon'" in dep_tools.get_all_messages()[0]
Exemplo n.º 8
0
def test_scalar_parallelise(parser):
    '''Tests the scalar checks of can_loop_be_parallelised.
    '''
    reader = FortranStringReader('''program test
                                 integer :: ji, jj, b
                                 integer, parameter :: jpi=7, jpj=9
                                 integer, dimension(jpi,jpj) :: a, c
                                 do jj = 1, jpj   ! loop 0
                                    do ji = 1, jpi
                                       a(ji, jj) = b
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 1
                                    do ji = 1, jpi
                                       b = a(ji, jj)
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 2
                                    do ji = 1, jpi
                                       b = a(ji, jj)
                                       c(ji, jj) = b*b
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 3
                                    do ji = 1, jpi
                                       b = b + a(ji, jj)
                                     end do
                                 end do
                                 end program test''')
    prog = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(prog)
    loops = psy.invokes.get("test").schedule
    dep_tools = DependencyTools(["levels", "lat"])

    # Read only scalar variable: a(ji, jj) = b
    parallel = dep_tools.can_loop_be_parallelised(loops[0], "jj")
    assert parallel

    # Write only scalar variable: a(ji, jj) = b
    parallel = dep_tools.can_loop_be_parallelised(loops[1], "jj")
    assert not parallel
    assert "Scalar variable 'b' is only written once" \
        in dep_tools.get_all_messages()[0]

    # Write to scalar variable happens first
    parallel = dep_tools.can_loop_be_parallelised(loops[2], "jj")
    assert parallel

    # Reduction operation on scalar variable
    parallel = dep_tools.can_loop_be_parallelised(loops[3], "jj")
    assert not parallel
    assert "Variable 'b' is read first, which indicates a reduction."\
        in dep_tools.get_all_messages()[0]
Exemplo n.º 9
0
def test_nested_loop_detection(parser):
    '''Tests if nested loop are handled correctly.
    '''
    reader = FortranStringReader('''program test
                                 do jk = 1, jpk   ! loop 0
                                   umask(1,1,jk) = -1.0d0
                                 end do
                                 do ji = 1, jpi   ! loop 1
                                   xmask(ji,1,1) = -1.0d0
                                 end do
                                 end program test''')
    prog = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(prog)
    loops = psy.invokes.get("test").schedule
    dep_tools = DependencyTools(["levels", "lat"])

    # Not a nested loop
    parallel = dep_tools.can_loop_be_parallelised(loops[0], "jk")
    assert not parallel
    assert "Not a nested loop" in dep_tools.get_all_messages()[0]

    # Now disable the test for nested loops:
    parallel = dep_tools.can_loop_be_parallelised(loops[0], "jk", False)
    assert parallel
    # Make sure can_loop_be_parallelised clears old messages automatically
    assert not dep_tools.get_all_messages()
Exemplo n.º 10
0
    def gen_code(self, parent):
        # pylint: disable=arguments-differ
        '''
        Generates the code required for extraction of one or more Nodes.
        It uses the PSyData API (via the base class PSyDataNode) to create
        the required callbacks that will allow a library to write the
        kernel data to a file.

        :param parent: the parent of this Node in the PSyIR.
        :type parent: :py:class:`psyclone.psyir.nodes.Node`.
        '''

        # Determine the variables to write:
        from psyclone.psyir.tools.dependency_tools import DependencyTools
        dep = DependencyTools()
        self._input_list, self._output_list = dep.get_in_out_parameters(self)

        # Add a callback here so that derived classes can adjust the list
        # of variables to provide, or the suffix used (which might
        # depend on the variable name which could create clashes).
        self.update_vars_and_postname()

        options = {
            'pre_var_list': self._input_list,
            'post_var_list': self._output_list,
            'post_var_postfix': self._post_name
        }

        from psyclone.f2pygen import CommentGen
        parent.add(CommentGen(parent, ""))
        parent.add(CommentGen(parent, " ExtractStart"))
        parent.add(CommentGen(parent, ""))
        super(ExtractNode, self).gen_code(parent, options)
        parent.add(CommentGen(parent, ""))
        parent.add(CommentGen(parent, " ExtractEnd"))
        parent.add(CommentGen(parent, ""))
Exemplo n.º 11
0
def test_messages():
    '''Tests the messaging system of the dependency tools.'''

    dep_tools = DependencyTools()
    assert dep_tools.get_all_messages() == []
    dep_tools._add_info("info-test")
    assert dep_tools.get_all_messages()[0] == "Info: info-test"
    dep_tools._add_warning("warning-test")
    assert dep_tools.get_all_messages()[1] == "Warning: warning-test"
    dep_tools._add_error("error-test")
    assert dep_tools.get_all_messages()[2] == "Error: error-test"

    dep_tools._clear_messages()
    assert dep_tools.get_all_messages() == []
Exemplo n.º 12
0
def test_derived_type(parser):
    '''Tests assignment to derived type variables. For now (see #363)
    they are only partially supported, and as default assume that these
    kind of statements are not parallelisable.
    '''
    reader = FortranStringReader('''program test
                                 do jj = 1, jpj   ! loop 0
                                    do ji = 1, jpi
                                       a%b(ji, jj) = 0
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 0
                                    do ji = 1, jpi
                                       a%b(ji, jj) = 0
                                       b%b(ji, jj) = 0
                                     end do
                                 end do
                                 end program test''')
    prog = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(prog)
    loops = psy.invokes.get("test").schedule
    dep_tools = DependencyTools(["levels", "lat"])

    parallel = dep_tools.can_loop_be_parallelised(loops[0], "jj")
    assert not parallel
    assert "Assignment to derived type 'a % b(ji, jj)' is not supported yet" \
           in dep_tools.get_all_messages()[0]

    # Test that testing is stopped with the first unparallelisable statement
    parallel = dep_tools.can_loop_be_parallelised(loops[1], "jj")
    assert not parallel
    # Test that only one message is stored, i.e. no message for the
    # next assignment to a derived type.
    assert len(dep_tools.get_all_messages()) == 1
    assert "Assignment to derived type 'a % b(ji, jj)' is not supported yet" \
           in dep_tools.get_all_messages()[0]

    parallel = dep_tools.can_loop_be_parallelised(loops[1],
                                                  "jj",
                                                  test_all_variables=True)
    assert not parallel
    # Now we must have two messages, one for each of the two assignments
    assert len(dep_tools.get_all_messages()) == 2
    assert "Assignment to derived type 'a % b(ji, jj)' is not supported yet" \
           in dep_tools.get_all_messages()[0]
    assert "Assignment to derived type 'b % b(ji, jj)' is not supported yet" \
           in dep_tools.get_all_messages()[1]

    # Test that variables are ignored as expected.
    parallel = dep_tools.\
        can_loop_be_parallelised(loops[1], "jj", variables_to_ignore=["a % b"])
    assert not parallel
    assert len(dep_tools.get_all_messages()) == 1
    assert "Assignment to derived type 'b % b(ji, jj)' is not supported yet" \
           in dep_tools.get_all_messages()[0]

    # If both derived types are ignored, the loop should be marked
    # to be parallelisable
    parallel = dep_tools.\
        can_loop_be_parallelised(loops[1], "jj",
                                 variables_to_ignore=["a % b", "b % b"])
    assert parallel
Exemplo n.º 13
0
def test_scalar_parallelise(declaration, variable, parser):
    '''Tests that scalar variables are correctly handled. It tests for
    'normal' scalar variables ('b') as well as scalar variables in derived
    types ('b%b').

    :param str declaration: the declaration of the variable (e.g. \
        'integer :: b', or 'type(my_type) :: b'')
    :param str variable: the variable (e.g. 'b', or 'b%b')

    '''
    reader = FortranStringReader('''program test
                                 implicit none
                                 type :: my_sub_type
                                    integer :: scalar
                                 end type my_sub_type
                                 type :: my_type
                                    integer :: scalar
                                    type(my_sub_type) :: sub
                                 end type my_type

                                 {0}  ! Declaration of variable here
                                 integer :: ji, jj
                                 integer, parameter :: jpi=7, jpj=9
                                 integer, dimension(jpi,jpj) :: b, c
                                 do jj = 1, jpj   ! loop 0
                                    do ji = 1, jpi
                                       b(ji, jj) = {1}
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 1
                                    do ji = 1, jpi
                                       {1} = b(ji, jj)
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 2
                                    do ji = 1, jpi
                                       {1} = b(ji, jj)
                                       c(ji, jj) = {1}*{1}
                                     end do
                                 end do
                                 do jj = 1, jpj   ! loop 3
                                    do ji = 1, jpi
                                       {1} = {1} + b(ji, jj)
                                     end do
                                 end do
                                 end program test'''.format(
        declaration, variable))
    prog = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(prog)
    loops = psy.invokes.get("test").schedule
    dep_tools = DependencyTools(["levels", "lat"])

    # Read only scalar variable: a(ji, jj) = b
    parallel = dep_tools.can_loop_be_parallelised(loops[0], "jj")
    assert parallel

    # Write only scalar variable: a(ji, jj) = b
    parallel = dep_tools.can_loop_be_parallelised(loops[1], "jj")
    assert not parallel
    assert "Scalar variable '{0}' is only written once".format(variable) \
        in dep_tools.get_all_messages()[0]

    # Write to scalar variable happens first
    parallel = dep_tools.can_loop_be_parallelised(loops[2], "jj")
    assert parallel

    # Reduction operation on scalar variable
    parallel = dep_tools.can_loop_be_parallelised(loops[3], "jj")
    assert not parallel
    assert "Variable '{0}' is read first, which indicates a reduction."\
        .format(variable) in dep_tools.get_all_messages()[0]