Esempio n. 1
0
def test_globalstoargumentstrans_clash_symboltable(monkeypatch):
    ''' Check the GlobalsToArguments transformation with a symbol name clash
    produces the expected error.'''

    trans = KernelGlobalsToArguments()
    # Construct a testing InvokeSchedule
    _, invoke_info = parse(os.path.join(BASEPATH, "gocean1p0",
                                        "single_invoke_kern_with_use.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    kernel = invoke.schedule.coded_kernels()[0]

    # Monkeypatch Symbol.resolve_deferred to avoid module searching and
    # importing in this test. In this case we assume the symbol is a
    # DataSymbol of REAL type.
    def create_real(variable):
        return DataSymbol(variable.name,
                          REAL_TYPE,
                          interface=variable.interface)

    monkeypatch.setattr(Symbol, "resolve_deferred", create_real)

    # Add 'rdt' into the symbol table
    kernel.root.symbol_table.add(DataSymbol("rdt", REAL_TYPE))

    # Test transforming a single kernel
    with pytest.raises(KeyError) as err:
        trans.apply(kernel)
    assert ("Couldn't copy 'rdt: <Scalar<REAL, UNDEFINED>, "
            "Global(container='model_mod')>' into the SymbolTable. The name "
            "'rdt' is already used by another symbol." in str(err.value))
Esempio n. 2
0
def trans(psy):
    '''
    Transformation routine for use with PSyclone. Applies the OpenCL
    transform to the first Invoke in the psy object.

    :param psy: the PSy object which this script will transform.
    :type psy: :py:class:`psyclone.psyGen.PSy`
    :returns: the transformed PSy object.
    :rtype: :py:class:`psyclone.psyGen.PSy`

    '''

    # Get the Schedule associated with the first Invoke
    invoke = psy.invokes.invoke_list[0]
    sched = invoke.schedule

    # Convert any kernel accesses to global data into arguments
    ktrans = KernelGlobalsToArguments()
    for kern in sched.kernels():
        ktrans.apply(kern)

    # Transform the Schedule
    cltrans = OCLTrans()
    cltrans.apply(sched, options={"end_barrier": True})

    # Provide kernel-specific OpenCL optimization options
    move_boundaries_trans = GOMoveIterationBoundariesInsideKernelTrans()
    for kern in sched.kernels():
        # Move the PSy-layer loop boundaries inside the kernel as a kernel
        # mask, this allows to iterate through the whole domain
        move_boundaries_trans.apply(kern)
        # Specify the OpenCL queue and workgroup size of the kernel
        kern.set_opencl_options({"queue_number": 1, 'local_size': 4})

    return psy
Esempio n. 3
0
def test_globalstoargumentstrans_unsupported_gocean_scalar(monkeypatch):
    ''' Check the GlobalsToArguments transformation when the global is
    a type not supported by the GOcean infrastructure raises an Error'''

    trans = KernelGlobalsToArguments()

    # Construct a testing InvokeSchedule
    _, invoke_info = parse(os.path.join(BASEPATH, "gocean1p0",
                                        "single_invoke_kern_with_use.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    kernel = invoke.schedule.coded_kernels()[0]

    # In this case we set it to be of type CHARACTER as that is not supported
    # in the GOcean infrastructure.
    def create_data_symbol(arg):
        symbol = DataSymbol(arg.name, CHARACTER_TYPE, interface=arg.interface)
        return symbol

    monkeypatch.setattr(Symbol, "resolve_deferred", create_data_symbol)

    # Test transforming a single kernel
    with pytest.raises(TypeError) as err:
        trans.apply(kernel)
    assert ("The global variable 'rdt' could not be promoted to an argument "
            "because the GOcean infrastructure does not have any scalar type "
            "equivalent to the PSyIR Scalar<CHARACTER, UNDEFINED> type."
            in str(err.value))
Esempio n. 4
0
def trans(psy):
    '''
    Transformation routine for use with PSyclone. Applies the OpenCL
    transform to the first Invoke in the psy object.

    :param psy: the PSy object which this script will transform.
    :type psy: :py:class:`psyclone.psyGen.PSy`
    :returns: the transformed PSy object.
    :rtype: :py:class:`psyclone.psyGen.PSy`

    '''
    from psyclone.transformations import OCLTrans, KernelGlobalsToArguments

    # Get the Schedule associated with the first Invoke
    invoke = psy.invokes.invoke_list[0]
    sched = invoke.schedule

    # Convert any kernel accesses to global data into arguments
    ktrans = KernelGlobalsToArguments()
    for kern in sched.kernels():
        ktrans.apply(kern)

    # Transform the Schedule
    cltrans = OCLTrans()
    cltrans.apply(sched, options={"end_barrier": True})

    # Provide kernel-specific OpenCL optimization options
    for kern in sched.kernels():
        kern.set_opencl_options({"queue_number": 1, 'local_size': 4})

    return psy
Esempio n. 5
0
def test_globalstoargumentstrans_no_wildcard_import():
    ''' Check that the transformation rejects kernels with wildcard
    imports. '''
    trans = KernelGlobalsToArguments()
    path = os.path.join(BASEPATH, "gocean1p0")
    _, invoke_info = parse(os.path.join(
        path, "single_invoke_kern_with_unqualified_use.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    kernel = invoke.schedule.coded_kernels()[0]
    with pytest.raises(TransformationError) as err:
        trans.apply(kernel)
    assert ("'kernel_with_use_code' has a wildcard import of symbols from "
            "container 'model_mod'" in str(err.value))
Esempio n. 6
0
def test_globalstoargumentstrans_no_outer_module_import():
    ''' Check that we reject kernels that access data that is declared in the
    enclosing module. '''
    trans = KernelGlobalsToArguments()
    path = os.path.join(BASEPATH, "gocean1p0")
    _, invoke_info = parse(os.path.join(path,
                                        "single_invoke_kern_with_global.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    kernel = invoke.schedule.coded_kernels()[0]
    with pytest.raises(TransformationError) as err:
        trans.apply(kernel)
    assert ("accesses a variable that is not declared in local scope"
            in str(err.value))
Esempio n. 7
0
def test_globalstoargumentstrans_wrongapi():
    ''' Check the KernelGlobalsToArguments with an API other than GOcean1p0'''

    trans = KernelGlobalsToArguments()
    path = os.path.join(BASEPATH, "dynamo0p3")
    _, invoke_info = parse(os.path.join(path, "1_single_invoke.f90"),
                           api="dynamo0.3")
    psy = PSyFactory("dynamo0.3").create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    kernel = invoke.schedule.coded_kernels()[0]
    with pytest.raises(TransformationError) as err:
        trans.apply(kernel)
    assert "The KernelGlobalsToArguments transformation is currently only " \
           "supported for the GOcean API but got an InvokeSchedule of " \
           "type:" in str(err.value)
Esempio n. 8
0
def test_globalstoarguments_noglobals():
    ''' Check the KernelGlobalsToArguments transformation can be applied to
    a kernel that does not contain any global without any effect '''

    # Parse a file to get an initialised GOKernelsArguments object
    _, invoke_info = parse(os.path.join(BASEPATH, "gocean1p0",
                                        "single_invoke.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    kernel = invoke.schedule.coded_kernels()[0]
    trans = KernelGlobalsToArguments()
    before_code = str(psy.gen)
    trans.apply(kernel)
    after_code = str(psy.gen)
    assert before_code == after_code
Esempio n. 9
0
def trans(psy):
    ''' Take the supplied psy object, apply OpenACC transformations
    to the schedule of the first invoke and return the new psy object '''
    from psyclone.transformations import ACCParallelTrans, \
        ACCEnterDataTrans, ACCLoopTrans, ACCRoutineTrans, \
        KernelGlobalsToArguments
    ptrans = ACCParallelTrans()
    ltrans = ACCLoopTrans()
    dtrans = ACCEnterDataTrans()
    ktrans = ACCRoutineTrans()
    g2localtrans = KernelGlobalsToArguments()

    invoke = psy.invokes.invoke_list[0]
    schedule = invoke.schedule
    schedule.view()

    # Apply the OpenACC Loop transformation to *every* loop
    # nest in the schedule
    from psyclone.psyir.nodes import Loop
    for child in schedule.children:
        if isinstance(child, Loop):
            newschedule, _ = ltrans.apply(child, {"collapse": 2})
            schedule = newschedule

    # Put all of the loops in a single parallel region
    newschedule, _ = ptrans.apply(schedule.children)

    # Add an enter-data directive
    newschedule, _ = dtrans.apply(schedule)

    # Convert any accesses to global data into kernel arguments and then
    # put an 'acc routine' directive inside each kernel
    for kern in schedule.coded_kernels():
        if kern.name == "kern_use_var_code":
            # TODO #490 and #663. This currently won't work because the
            # KernelGlobalsToArguments transformation works on the PSyIR but
            # the subsequent ACCRoutineTrans works on the fparser2 parse tree.
            g2localtrans.apply(kern)
        _, _ = ktrans.apply(kern)
        # Ideally we would module-inline the kernel here (to save having to
        # rely on the compiler to do it) but this does not currently work
        # for the fparser2 AST (issue #229).
        # _, _ = itrans.apply(kern)

    invoke.schedule = newschedule
    newschedule.view()
    return psy
Esempio n. 10
0
def trans(psy):
    ''' Take the supplied psy object, apply OpenACC transformations
    to the schedule of the first invoke and return the new psy object '''
    from psyclone.transformations import ACCParallelTrans, \
        ACCEnterDataTrans, ACCLoopTrans, ACCRoutineTrans, \
        KernelGlobalsToArguments, KernelModuleInlineTrans
    ptrans = ACCParallelTrans()
    ltrans = ACCLoopTrans()
    dtrans = ACCEnterDataTrans()
    ktrans = ACCRoutineTrans()
    itrans = KernelModuleInlineTrans()
    g2localtrans = KernelGlobalsToArguments()

    invoke = psy.invokes.invoke_list[0]
    schedule = invoke.schedule
    schedule.view()

    # Apply the OpenACC Loop transformation to *every* loop
    # nest in the schedule
    from psyclone.psyir.nodes import Loop
    for child in schedule.children:
        if isinstance(child, Loop):
            newschedule, _ = ltrans.apply(child, {"collapse": 2})
            schedule = newschedule

    # Put all of the loops in a single parallel region
    newschedule, _ = ptrans.apply(schedule.children)

    # Add an enter-data directive
    newschedule, _ = dtrans.apply(schedule)

    # Convert any accesses to global data into kernel arguments, put an
    # 'acc routine' directive inside, and module-inline each kernel
    for kern in schedule.coded_kernels():
        if kern.name == "kern_use_var_code":
            g2localtrans.apply(kern)
        _, _ = ktrans.apply(kern)
        _, _ = itrans.apply(kern)

    invoke.schedule = newschedule
    newschedule.view()
    return psy
Esempio n. 11
0
def test_globalstoargumentstrans_constant(monkeypatch):
    ''' Check the GlobalsToArguments transformation when the global is
    also a constant value, in this case the argument should be read-only.'''
    from psyclone.psyir.backend.fortran import FortranWriter
    from psyclone.psyir.nodes import Literal

    trans = KernelGlobalsToArguments()

    # Construct a testing InvokeSchedule
    _, invoke_info = parse(os.path.join(BASEPATH, "gocean1p0",
                                        "single_invoke_kern_with_use.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    kernel = invoke.schedule.coded_kernels()[0]

    # Monkeypatch resolve_deferred to avoid module searching and importing
    # in this test. In this case we assume it is a constant INTEGER
    def create_data_symbol(arg):
        symbol = DataSymbol(arg.name,
                            INTEGER_TYPE,
                            interface=arg.interface,
                            constant_value=Literal("1", INTEGER_TYPE))
        return symbol

    monkeypatch.setattr(DataSymbol, "resolve_deferred", create_data_symbol)
    monkeypatch.setattr(Symbol, "resolve_deferred", create_data_symbol)

    # Test transforming a single kernel
    trans.apply(kernel)

    fwriter = FortranWriter()
    kernel_code = fwriter(kernel.get_kernel_schedule())

    assert "subroutine kernel_with_use_code(ji, jj, istep, ssha, tmask, rdt)" \
        in kernel_code
    assert "integer, intent(in) :: rdt" in kernel_code
Esempio n. 12
0
def test_globalstoarguments_multiple_kernels(monkeypatch):
    ''' Check the KernelGlobalsToArguments transformation with an invoke with
    three kernel calls, two of them duplicated and the third one sharing the
    same imported module'''
    from psyclone.psyir.backend.fortran import FortranWriter
    fwriter = FortranWriter()

    # Construct a testing InvokeSchedule
    _, invoke_info = parse(os.path.join(
        BASEPATH, "gocean1p0", "single_invoke_three_kernels_with_use.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    trans = KernelGlobalsToArguments()

    # The kernels are checked before the psy.gen, so they don't include the
    # modified suffix.
    expected = [
        [
            "subroutine kernel_with_use_code(ji, jj, istep, ssha, tmask, rdt)",
            "real, intent(inout) :: rdt"
        ],
        [
            "subroutine kernel_with_use2_code(ji, jj, istep, ssha, tmask, cbfr,"
            " rdt)",
            "real, intent(inout) :: cbfr\n  real, intent(inout) :: rdt"
        ],
        [
            "subroutine kernel_with_use_code(ji, jj, istep, ssha, tmask, rdt)",
            "real, intent(inout) :: rdt"
        ]
    ]

    # Monkeypatch the resolve_deferred() methods to avoid searching and
    # importing of module during this test.
    def create_data_symbol(arg):
        symbol = DataSymbol(arg.name, REAL_TYPE, interface=arg.interface)
        return symbol

    monkeypatch.setattr(Symbol, "resolve_deferred", create_data_symbol)
    monkeypatch.setattr(DataSymbol, "resolve_deferred", create_data_symbol)

    for num, kernel in enumerate(invoke.schedule.coded_kernels()):
        kschedule = kernel.get_kernel_schedule()

        trans.apply(kernel)

        # Check the kernel code is generated as expected
        kernel_code = fwriter(kschedule)
        assert expected[num][0] in kernel_code
        assert expected[num][1] in kernel_code

    generated_code = str(psy.gen)

    # The following assert checks that globals from the same module are
    # imported, since the kernels are marked as modified, new suffixes are
    # given in order to differentiate each of them.
    assert "SUBROUTINE invoke_0(oldu_fld, cu_fld)\n" \
           "      USE kernel_with_use_1_mod, ONLY: kernel_with_use_1_code\n" \
           "      USE kernel_with_use2_0_mod, ONLY:" \
           " kernel_with_use2_0_code\n" \
           "      USE kernel_with_use_0_mod, ONLY:" \
           " kernel_with_use_0_code\n" in generated_code

    # Check the kernel calls have the global passed as last argument
    assert "CALL kernel_with_use_0_code(i, j, oldu_fld, cu_fld%data, " \
           "cu_fld%grid%tmask, rdt)" in generated_code
    assert "CALL kernel_with_use_1_code(i, j, oldu_fld, cu_fld%data, " \
           "cu_fld%grid%tmask, rdt)" in generated_code
    assert "CALL kernel_with_use2_0_code(i, j, oldu_fld, cu_fld%data, " \
           "cu_fld%grid%tmask, cbfr, rdt)" in generated_code
Esempio n. 13
0
def test_globalstoargumentstrans(monkeypatch):
    ''' Check the GlobalsToArguments transformation with a single kernel
    invoke and a global variable.'''
    from psyclone.psyGen import Argument
    from psyclone.psyir.backend.fortran import FortranWriter

    trans = KernelGlobalsToArguments()
    assert trans.name == "KernelGlobalsToArguments"
    assert str(trans) == "Convert the global variables used inside the " \
        "kernel into arguments and modify the InvokeSchedule to pass them" \
        " in the kernel call."

    # Construct a testing InvokeSchedule
    _, invoke_info = parse(os.path.join(BASEPATH, "gocean1p0",
                                        "single_invoke_kern_with_use.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    notkernel = invoke.schedule.children[0]
    kernel = invoke.schedule.coded_kernels()[0]

    # Monkeypatch resolve_deferred to avoid module searching and importing
    # in this test. In this case we assume it is a REAL
    def set_to_real(variable):
        variable._datatype = REAL_TYPE

    monkeypatch.setattr(DataSymbol, "resolve_deferred", set_to_real)

    # Test with invalid node
    with pytest.raises(TransformationError) as err:
        trans.apply(notkernel)
    assert ("The KernelGlobalsToArguments transformation can only be applied"
            " to CodedKern nodes but found 'GOLoop' instead."
            in str(err.value))

    # Test transforming a single kernel
    trans.apply(kernel)

    assert kernel.modified

    # The transformation;
    # 1) Has imported the symbol into the InvokeSchedule
    assert invoke.schedule.symbol_table.lookup("rdt")
    assert invoke.schedule.symbol_table.lookup("model_mod")
    var = invoke.schedule.symbol_table.lookup("rdt")
    container = invoke.schedule.symbol_table.lookup("model_mod")
    assert var.is_global
    assert var.interface.container_symbol == container

    # 2) Has added the symbol as the last argument in the kernel call
    assert isinstance(kernel.args[-1], Argument)
    assert kernel.args[-1].name == "rdt"

    # 3) Has converted the Kernel Schedule symbol into an argument which is
    # in also the last position
    ksymbol = kernel.get_kernel_schedule().symbol_table.lookup("rdt")
    assert ksymbol.is_argument
    assert kernel.get_kernel_schedule().symbol_table.argument_list[-1] == \
        ksymbol
    assert len(kernel.get_kernel_schedule().symbol_table.argument_list) == \
        len(kernel.args) + 2  # GOcean kernels have 2 implicit arguments

    # Check the kernel code is generated as expected
    fwriter = FortranWriter()
    kernel_code = fwriter(kernel.get_kernel_schedule())
    assert "subroutine kernel_with_use_code(ji,jj,istep,ssha,tmask,rdt)" \
        in kernel_code
    assert "real, intent(inout) :: rdt" in kernel_code

    # Check that the PSy-layer generated code now contains the use statement
    # and argument call
    generated_code = str(psy.gen)
    assert "USE model_mod, ONLY: rdt" in generated_code
    assert "CALL kernel_with_use_code(i, j, oldu_fld, cu_fld%data, " \
           "cu_fld%grid%tmask, rdt)" in generated_code
    assert invoke.schedule.symbol_table.lookup("model_mod")
    assert invoke.schedule.symbol_table.lookup("rdt")