예제 #1
0
def test_no_inline_before_trans(monkeypatch, tmpdir):
    ''' Check that we reject attempts to transform kernels that have been
    marked for module in-lining. Issue #229. '''

    # Even though this code will raise an exception at the end, it will
    # still create an (empty) file 'testkern_any_space_2_0_mod.f90'.
    # So change to tmpdir to avoid this.
    old_pwd = tmpdir.chdir()
    from psyclone.transformations import KernelModuleInlineTrans
    psy, invoke = get_invoke("4.5.2_multikernel_invokes.f90", api="dynamo0.3",
                             idx=0)
    sched = invoke.schedule
    kernels = sched.walk(Kern)
    assert len(kernels) == 5
    inline_trans = KernelModuleInlineTrans()
    rtrans = ACCRoutineTrans()
    _, _ = inline_trans.apply(kernels[1])
    with pytest.raises(TransformationError) as err:
        _, _ = rtrans.apply(kernels[1])
    assert "because it will be module-inlined" in str(err)
    # Monkeypatch the validate() routine so we can check that we catch
    # the error at code-generation time
    monkeypatch.setattr(rtrans, "validate", lambda kern: None)
    _, _ = rtrans.apply(kernels[1])
    with pytest.raises(NotImplementedError) as err:
        _ = str(psy.gen).lower()
    assert "Cannot module-inline a transformed kernel " in str(err)
    old_pwd.chdir()
예제 #2
0
def trans(psy):
    ''' Take the supplied psy object, apply OpenACC transformations
    to the schedule of invoke_0 and return the new psy object '''
    ptrans = ACCParallelTrans()
    ltrans = ACCLoopTrans()
    dtrans = ACCEnterDataTrans()
    ktrans = ACCRoutineTrans()

    invoke = psy.invokes.get('invoke_0_inc_field')
    schedule = invoke.schedule
    schedule.view()

    # Apply the OpenACC Loop transformation to *every* loop
    # nest in the schedule
    for child in schedule.children:
        if isinstance(child, Loop):
            ltrans.apply(child, {"collapse": 2})

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

    # Add an enter-data directive
    dtrans.apply(schedule)

    # Put an 'acc routine' directive inside each kernel
    for kern in schedule.coded_kernels():
        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)

    schedule.view()
    return psy
예제 #3
0
def test_accroutine_rejects_transformed_kernel():
    ''' Check that the ACCRoutineTrans rejects an already-transformed
    kernel (because it still works with the fparser2 parse tree and not
    the PSyIR - Issue #490). '''
    rtrans = ACCRoutineTrans()
    _, invoke = get_invoke("nemolite2d_alg_mod.f90", api="gocean1.0", idx=0)
    sched = invoke.schedule
    kern = sched.coded_kernels()[0]
    # Pretend that this kernel has previously been transformed
    kern.modified = True
    with pytest.raises(TransformationError) as err:
        rtrans.apply(kern)
    assert ("Cannot transform kernel 'continuity_code' because it has "
            "previously been transformed" in str(err.value))
예제 #4
0
def test_1kern_trans(kernel_outputdir):
    ''' Check that we generate the correct code when an invoke contains
    the same kernel more than once but only one of them is transformed. '''
    psy, invoke = get_invoke("4_multikernel_invokes.f90",
                             api="dynamo0.3",
                             idx=0)
    sched = invoke.schedule
    kernels = sched.coded_kernels()
    # We will transform the second kernel but not the first
    kern = kernels[1]
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kern)
    # Generate the code (this triggers the generation of a new kernel)
    code = str(psy.gen).lower()
    tag = re.search('use testkern(.+?)_mod', code).group(1)
    # We should have a USE for the original kernel and a USE for the new one
    assert "use testkern{0}_mod, only: testkern{0}_code".format(tag) in code
    assert "use testkern_mod, only: testkern_code" in code
    # Similarly, we should have calls to both the original and new kernels
    assert "call testkern_code(" in code
    assert "call testkern{0}_code(".format(tag) in code
    first = code.find("call testkern_code(")
    second = code.find("call testkern{0}_code(".format(tag))
    assert first < second
    assert LFRicBuild(kernel_outputdir).code_compiles(psy)
예제 #5
0
def test_new_kern_single_error(tmpdir, monkeypatch):
    ''' Check that we do not overwrite an existing, different kernel if
    there is a name clash and kernel-naming is 'single'. '''
    # Ensure kernel-output directory is uninitialised
    config = Config.get()
    monkeypatch.setattr(config, "_kernel_output_dir", "")
    monkeypatch.setattr(config, "_kernel_naming", "single")
    # Change to temp dir (so kernel written there)
    old_cwd = tmpdir.chdir()
    _, invoke = get_invoke("1_single_invoke.f90", api="dynamo0.3", idx=0)
    sched = invoke.schedule
    kernels = sched.coded_kernels()
    kern = kernels[0]
    old_mod_name = kern.module_name[:]
    # Create a file with the same name as we would otherwise generate
    with open(os.path.join(str(tmpdir),
                           old_mod_name+"_0_mod.f90"), "w") as ffile:
        ffile.write("some code")
    rtrans = ACCRoutineTrans()
    new_kern, _ = rtrans.apply(kern)
    # Generate the code - this should raise an error as we get a name
    # clash and the content of the existing file is not the same as that
    # which we would generate
    with pytest.raises(GenerationError) as err:
        new_kern.rename_and_write()
    assert ("transformed version of this Kernel 'testkern_0_mod.f90' already "
            "exists in the kernel-output directory ({0}) but is not the same "
            "as the current, transformed kernel and the kernel-renaming "
            "scheme is set to 'single'".format(str(tmpdir)) in str(err))
    old_cwd.chdir()
예제 #6
0
def test_new_same_kern_single(tmpdir, monkeypatch):
    ''' Check that we do not overwrite an existing, identical kernel if
    there is a name clash and kernel-naming is 'single'. '''
    # Ensure kernel-output directory is uninitialised
    config = Config.get()
    monkeypatch.setattr(config, "_kernel_output_dir", "")
    monkeypatch.setattr(config, "_kernel_naming", "single")
    rtrans = ACCRoutineTrans()
    # Change to temp dir (so kernel written there)
    old_cwd = tmpdir.chdir()
    _, invoke = get_invoke("4_multikernel_invokes.f90", api="dynamo0.3",
                           idx=0)
    sched = invoke.schedule
    # Apply the same transformation to both kernels. This should produce
    # two, identical transformed kernels.
    new_kernels = []
    for kern in sched.coded_kernels():
        new_kern, _ = rtrans.apply(kern)
        new_kernels.append(new_kern)

    # Generate the code - we should end up with just one transformed kernel
    new_kernels[0].rename_and_write()
    new_kernels[1].rename_and_write()
    assert new_kernels[1]._name == "testkern_0_code"
    assert new_kernels[1].module_name == "testkern_0_mod"
    out_files = os.listdir(str(tmpdir))
    assert out_files == [new_kernels[1].module_name+".f90"]
    old_cwd.chdir()
예제 #7
0
def test_1kern_trans(tmpdir, monkeypatch):
    ''' Check that we generate the correct code when an invoke contains
    the same kernel more than once but only one of them is transformed. '''
    # Ensure kernel-output directory is uninitialised
    config = Config.get()
    monkeypatch.setattr(config, "_kernel_output_dir", "")
    # Change to temp dir (so kernel written there)
    old_cwd = tmpdir.chdir()
    psy, invoke = get_invoke("4_multikernel_invokes.f90", api="dynamo0.3",
                             idx=0)
    sched = invoke.schedule
    kernels = sched.coded_kernels()
    # We will transform the second kernel but not the first
    kern = kernels[1]
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kern)
    # Generate the code (this triggers the generation of a new kernel)
    code = str(psy.gen).lower()
    tag = re.search('use testkern(.+?)_mod', code).group(1)
    # We should have a USE for the original kernel and a USE for the new one
    assert "use testkern{0}_mod, only: testkern{0}_code".format(tag) in code
    assert "use testkern, only: testkern_code" in code
    # Similarly, we should have calls to both the original and new kernels
    assert "call testkern_code(" in code
    assert "call testkern{0}_code(".format(tag) in code
    first = code.find("call testkern_code(")
    second = code.find("call testkern{0}_code(".format(tag))
    assert first < second
    assert Dynamo0p3Build(tmpdir).code_compiles(psy)
    old_cwd.chdir()
예제 #8
0
def test_2kern_trans(tmpdir, monkeypatch):
    ''' Check that we generate correct code when we transform two kernels
    within a single invoke. '''
    # Ensure kernel-output directory is uninitialised
    config = Config.get()
    monkeypatch.setattr(config, "_kernel_output_dir", "")
    # Change to temp dir (so kernel written there)
    old_cwd = tmpdir.chdir()
    psy, invoke = get_invoke("4.5.2_multikernel_invokes.f90", api="dynamo0.3",
                             idx=0)
    sched = invoke.schedule
    kernels = sched.walk(Kern)
    assert len(kernels) == 5
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kernels[1])
    _, _ = rtrans.apply(kernels[2])
    # Generate the code (this triggers the generation of new kernels)
    code = str(psy.gen).lower()
    # Find the tags added to the kernel/module names
    for match in re.finditer('use testkern_any_space_2(.+?)_mod', code):
        tag = match.group(1)
        assert ("use testkern_any_space_2{0}_mod, only: "
                "testkern_any_space_2{0}_code".format(tag) in code)
        assert "call testkern_any_space_2{0}_code(".format(tag) in code
        assert os.path.isfile(
            os.path.join(str(tmpdir),
                         "testkern_any_space_2{0}_mod.f90".format(tag)))
    assert "use testkern_any_space_2_mod, only" not in code
    assert "call testkern_any_space_2_code(" not in code
    assert Dynamo0p3Build(tmpdir).code_compiles(psy)
    old_cwd.chdir()
예제 #9
0
def test_accroutine_err(monkeypatch):
    ''' Check that we raise the expected error if we can't find the
    source of the kernel subroutine. '''
    import fparser
    _, invoke = get_invoke("1_single_invoke.f90", api="dynamo0.3", idx=0)
    sched = invoke.schedule
    kernels = sched.coded_kernels()
    kern = kernels[0]
    assert isinstance(kern, Kern)
    # Edit the fparser1 AST of the kernel so that it does not have a
    # subroutine of the correct name
    ast = kern._module_code
    mod = ast.content[0]
    # Find the subroutine statement
    for child in mod.content:
        if isinstance(child, fparser.one.block_statements.Subroutine):
            sub = child
    # Find the end subroutine statement
    for child in sub.content:
        if isinstance(child, fparser.one.block_statements.EndSubroutine):
            end = child
    monkeypatch.setattr(sub, "name", "some_other_name")
    monkeypatch.setattr(end, "name", "some_other_name")
    rtrans = ACCRoutineTrans()
    with pytest.raises(TransformationError) as err:
        _ = rtrans.apply(kern)
    assert ("Failed to find subroutine source for kernel testkern_code"
            in str(err))
예제 #10
0
def test_new_kern_no_clobber(tmpdir, monkeypatch):
    ''' Check that we create a new kernel with a new name when kernel-naming
    is set to 'multiple' and we would otherwise get a name clash. '''
    # Ensure kernel-output directory is uninitialised
    config = Config.get()
    monkeypatch.setattr(config, "_kernel_output_dir", "")
    monkeypatch.setattr(config, "_kernel_naming", "multiple")
    # Change to temp dir (so kernel written there)
    old_cwd = tmpdir.chdir()
    psy, invoke = get_invoke("1_single_invoke.f90", api="dynamo0.3", idx=0)
    sched = invoke.schedule
    kernels = sched.walk(Kern)
    kern = kernels[0]
    old_mod_name = kern.module_name[:]
    # Create a file with the same name as we would otherwise generate
    with open(os.path.join(str(tmpdir),
                           old_mod_name+"_0_mod.f90"), "w") as ffile:
        ffile.write("some code")
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kern)
    # Generate the code (this triggers the generation of a new kernel)
    _ = str(psy.gen).lower()
    filename = os.path.join(str(tmpdir), old_mod_name+"_1_mod.f90")
    assert os.path.isfile(filename)
    old_cwd.chdir()
예제 #11
0
def test_accroutine():
    ''' Test that we can transform a kernel by adding a "!$acc routine"
    directive to it. '''
    from psyclone.gocean1p0 import GOKern
    from fparser.two import Fortran2003
    _, invoke = get_invoke("nemolite2d_alg_mod.f90", api="gocean1.0", idx=0)
    sched = invoke.schedule
    kern = sched.children[0].loop_body[0].loop_body[0]
    assert isinstance(kern, GOKern)
    rtrans = ACCRoutineTrans()
    assert rtrans.name == "ACCRoutineTrans"
    new_kern, _ = rtrans.apply(kern)
    # The transformation should have populated the fparser2 AST of
    # the kernel...
    assert new_kern._fp2_ast
    assert isinstance(new_kern._fp2_ast, Fortran2003.Program)
    # Check AST contains directive
    comments = walk_ast(new_kern._fp2_ast.content, [Fortran2003.Comment])
    assert len(comments) == 1
    assert str(comments[0]) == "!$acc routine"
    # Check that directive is in correct place (end of declarations)
    gen = str(new_kern._fp2_ast)
    assert ("REAL(KIND = go_wp), DIMENSION(:, :), INTENT(IN) :: sshn, sshn_u, "
            "sshn_v, hu, hv, un, vn\n"
            "    !$acc routine\n"
            "    ssha (ji, jj) = 0.0_go_wp\n" in gen)
예제 #12
0
def trans(psy):
    '''PSyclone transformation script for the dynamo0p3 api to apply
    OpenACC loop, parallel and enter data directives generically.

    :param psy: a PSyclone PSy object which captures the algorithm and \
        kernel information required by PSyclone.
    :type psy: subclass of :py:class:`psyclone.psyGen.PSy`

    '''
    kernels_trans = ACCKernelsTrans()
    routine_trans = ACCRoutineTrans()
    ctrans = Dynamo0p3ColourTrans()
    loop_trans = ACCLoopTrans()
    enter_trans = ACCEnterDataTrans()
    const = LFRicConstants()

    # Loop over all of the Invokes in the PSy object
    for invoke in psy.invokes.invoke_list:

        schedule = invoke.schedule

        # Colour loops as required
        for loop in schedule.loops():
            if loop.field_space.orig_name \
               not in const.VALID_DISCONTINUOUS_NAMES \
               and loop.iteration_space == "cell_column":
                ctrans.apply(loop)

        # Add Kernels and Loop directives
        for loop in schedule.loops():
            if loop.loop_type != "colours":
                kernels_trans.apply([loop])
                loop_trans.apply(loop)

        # Add Routine directive to kernels
        for kernel in schedule.coded_kernels():
            routine_trans.apply(kernel)

        # Add Enter Data directive covering all of the PSy layer.
        enter_trans.apply(schedule)

        schedule.view()

    return psy
예제 #13
0
def test_accroutine_empty_kernel():
    ''' Check that the directive goes at the end of the declarations,
    even when the rest of the kernel is empty. '''
    _, invoke = get_invoke("1_single_invoke.f90", api="dynamo0.3", idx=0)
    sched = invoke.schedule
    kernels = sched.coded_kernels()
    rtrans = ACCRoutineTrans()
    new_kern, _ = rtrans.apply(kernels[0])
    # Check that directive is in correct place (end of declarations)
    gen = str(new_kern._fp2_ast).lower()
    assert "!$acc routine\n  end subroutine testkern_code" in gen
예제 #14
0
def test_builtin_no_trans():
    ''' Check that we reject attempts to transform built-in kernels. '''
    from psyclone.dynamo0p3_builtins import DynBuiltIn
    _, invoke = get_invoke("15.1.1_X_plus_Y_builtin.f90",
                           api="dynamo0.3", idx=0)
    sched = invoke.schedule
    kernels = sched.walk(DynBuiltIn)
    rtrans = ACCRoutineTrans()
    with pytest.raises(TransformationError) as err:
        _ = rtrans.apply(kernels[0])
    assert ("ACCRoutineTrans to a built-in kernel is not yet supported and "
            "kernel 'x_plus_y' is of type " in str(err))
def test_accroutine_module_use():
    ''' Check that ACCRoutineTrans rejects a kernel if it contains a module
    use statement. '''
    _, invoke = get_invoke("single_invoke_kern_with_use.f90",
                           api="gocean1.0",
                           idx=0)
    sched = invoke.schedule
    kernels = sched.walk(Kern)
    rtrans = ACCRoutineTrans()
    with pytest.raises(TransformationError) as err:
        _ = rtrans.apply(kernels[0])
    assert "'global' scope: ['rdt']. PSyclone cannot currently" in str(err)
예제 #16
0
def test_accroutine_module_use():
    ''' Check that ACCRoutineTrans rejects a kernel if it contains a module
    use statement. '''
    _, invoke = get_invoke("single_invoke_kern_with_use.f90",
                           api="gocean1.0",
                           idx=0)
    sched = invoke.schedule
    kernels = sched.walk(Kern)
    rtrans = ACCRoutineTrans()
    with pytest.raises(TransformationError) as err:
        _ = rtrans.apply(kernels[0])
    assert ("global scope: ['rdt']. If these symbols represent data then they"
            " must first" in str(err.value))
예제 #17
0
def test_new_kernel_dir(kernel_outputdir):
    ''' Check that we write out the transformed kernel to a specified
    directory. '''
    psy, invoke = get_invoke("nemolite2d_alg_mod.f90", api="gocean1.0", idx=0)
    sched = invoke.schedule
    kern = sched.coded_kernels()[0]
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kern)
    # Generate the code (this triggers the generation of a new kernel)
    _ = str(psy.gen)
    file_list = os.listdir(str(kernel_outputdir))
    assert len(file_list) == 1
    assert file_list[0] == 'continuity_0_mod.f90'
예제 #18
0
def test_new_kernel_file(tmpdir, monkeypatch):
    ''' Check that we write out the transformed kernel to the CWD. '''
    from fparser.two import Fortran2003, parser
    from fparser.common.readfortran import FortranFileReader
    # Ensure kernel-output directory is uninitialised
    config = Config.get()
    monkeypatch.setattr(config, "_kernel_output_dir", "")
    monkeypatch.setattr(config, "_kernel_naming", "multiple")
    # Change to temp dir (so kernel written there)
    old_cwd = tmpdir.chdir()
    psy, invoke = get_invoke("nemolite2d_alg_mod.f90", api="gocean1.0", idx=0)
    sched = invoke.schedule
    kern = sched.children[0].loop_body[0].loop_body[0]
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kern)
    # Generate the code (this triggers the generation of a new kernel)
    code = str(psy.gen).lower()
    # Work out the value of the tag used to re-name the kernel
    tag = re.search('use continuity(.+?)_mod', code).group(1)
    assert ("use continuity{0}_mod, only: continuity{0}_code".format(tag)
            in code)
    assert "call continuity{0}_code(".format(tag) in code
    # The kernel and module name should have gained the tag just identified
    # and be written to the CWD
    filename = os.path.join(str(tmpdir), "continuity{0}_mod.f90".format(tag))
    assert os.path.isfile(filename)
    # Parse the new kernel file
    f2003_parser = parser.ParserFactory().create()
    reader = FortranFileReader(filename)
    prog = f2003_parser(reader)
    # Check that the module has the right name
    modules = walk_ast(prog.content, [Fortran2003.Module_Stmt])
    assert str(modules[0].items[1]) == "continuity{0}_mod".format(tag)
    # Check that the subroutine has the right name
    subs = walk_ast(prog.content, [Fortran2003.Subroutine_Stmt])
    found = False
    for sub in subs:
        if str(sub.items[1]) == "continuity{0}_code".format(tag):
            found = True
            break
    assert found
    # Check that the kernel type has been re-named
    dtypes = walk_ast(prog.content, [Fortran2003.Derived_Type_Def])
    names = walk_ast(dtypes[0].content, [Fortran2003.Type_Name])
    assert str(names[0]) == "continuity{0}_type".format(tag)

    from gocean1p0_build import GOcean1p0Build
    # If compilation fails this will raise an exception
    GOcean1p0Build(tmpdir).compile_file(filename)

    old_cwd.chdir()
예제 #19
0
def test_no_inline_before_trans(monkeypatch):
    ''' Check that we reject attempts to transform kernels that have been
    marked for module in-lining. Issue #229. '''
    from psyclone.transformations import KernelModuleInlineTrans
    psy, invoke = get_invoke("4.5.2_multikernel_invokes.f90",
                             api="dynamo0.3",
                             idx=0)
    sched = invoke.schedule
    kernels = sched.walk(sched.children, Kern)
    assert len(kernels) == 5
    inline_trans = KernelModuleInlineTrans()
    rtrans = ACCRoutineTrans()
    _, _ = inline_trans.apply(kernels[1])
    with pytest.raises(TransformationError) as err:
        _, _ = rtrans.apply(kernels[1])
    assert "because it will be module-inlined" in str(err)
    # Monkeypatch the validate() routine so we can check that we catch
    # the error at code-generation time
    monkeypatch.setattr(rtrans, "validate", lambda kern: None)
    _, _ = rtrans.apply(kernels[1])
    with pytest.raises(NotImplementedError) as err:
        _ = str(psy.gen).lower()
    assert "Cannot module-inline a transformed kernel " in str(err)
예제 #20
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 '''
    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
    for child in schedule.children:
        if isinstance(child, Loop):
            ltrans.apply(child, {"collapse": 2})

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

    # Add an enter-data directive
    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)

    schedule.view()
    return psy
예제 #21
0
def test_new_kernel_dir(tmpdir, monkeypatch):
    ''' Check that we write out the transformed kernel to a specified
    directory. '''
    # Set the output directory in the configuration object
    config = Config.get()
    monkeypatch.setattr(config, "_kernel_output_dir", str(tmpdir))
    psy, invoke = get_invoke("nemolite2d_alg_mod.f90", api="gocean1.0", idx=0)
    sched = invoke.schedule
    kern = sched.children[0].loop_body[0].loop_body[0]
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kern)
    # Generate the code (this triggers the generation of a new kernel)
    _ = str(psy.gen)
    file_list = os.listdir(str(tmpdir))
    assert len(file_list) == 1
    assert file_list[0] == 'continuity_0_mod.f90'
예제 #22
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
예제 #23
0
def trans(psy):
    ''' Take the supplied psy object, apply OpenACC transformations
    to the schedule of invoke_0 and return the new psy object '''
    from psyclone.transformations import ACCParallelTrans, \
        ACCDataTrans, ACCLoopTrans, ACCRoutineTrans, KernelModuleInlineTrans
    ptrans = ACCParallelTrans()
    ltrans = ACCLoopTrans()
    dtrans = ACCDataTrans()
    ktrans = ACCRoutineTrans()
    itrans = KernelModuleInlineTrans()

    invoke = psy.invokes.get('invoke_0_inc_field')
    schedule = invoke.schedule
    # schedule.view()

    # Apply the OpenACC Loop transformation to *every* loop
    # nest in the schedule
    from psyclone.psyGen 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)

    # Put an 'acc routine' directive inside each kernel
    for kern in schedule.kern_calls():
        _, _ = 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
예제 #24
0
def test_new_kernel_file(kernel_outputdir, monkeypatch, fortran_reader):
    ''' Check that we write out the transformed kernel to the CWD. '''
    # Ensure kernel-output directory is uninitialised
    config = Config.get()
    monkeypatch.setattr(config, "_kernel_naming", "multiple")
    psy, invoke = get_invoke("nemolite2d_alg_mod.f90", api="gocean1.0", idx=0)
    sched = invoke.schedule
    kern = sched.coded_kernels()[0]
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kern)
    # Generate the code (this triggers the generation of a new kernel)
    code = str(psy.gen).lower()
    # Work out the value of the tag used to re-name the kernel
    tag = re.search('use continuity(.+?)_mod', code).group(1)
    assert ("use continuity{0}_mod, only: continuity{0}_code".format(tag)
            in code)
    assert "call continuity{0}_code(".format(tag) in code
    # The kernel and module name should have gained the tag just identified
    # and be written to the CWD
    filename = os.path.join(str(kernel_outputdir),
                            "continuity{0}_mod.f90".format(tag))
    assert os.path.isfile(filename)
    # Parse the new kernel file
    psyir = fortran_reader.psyir_from_file(filename)
    # Check that the module has the right name
    assert isinstance(psyir, FileContainer)
    module = psyir.children[0]
    assert module.name == "continuity{0}_mod".format(tag)
    # Check that the subroutine has the right name
    found = False
    for sub in psyir.walk(Routine):
        if sub.name == "continuity{0}_code".format(tag):
            found = True
            break
    assert found

    from psyclone.tests.gocean1p0_build import GOcean1p0Build
    # If compilation fails this will raise an exception
    GOcean1p0Build(kernel_outputdir).compile_file(filename)
예제 #25
0
def test_no_inline_after_trans(monkeypatch):
    ''' Check that we reject attempts to inline a previously transformed
    kernel. Issue #229. '''
    from psyclone.transformations import KernelModuleInlineTrans
    _, invoke = get_invoke("4.5.2_multikernel_invokes.f90", api="dynamo0.3",
                           idx=0)
    sched = invoke.schedule
    kernels = sched.walk(Kern)
    assert len(kernels) == 5
    # Transform the kernel first
    inline_trans = KernelModuleInlineTrans()
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kernels[1])
    # Then attempt to inline it
    with pytest.raises(TransformationError) as err:
        _, _ = inline_trans.apply(kernels[1])
    assert "because it has previously been transformed" in str(err)
    # Monkeypatch the validate() routine so we can check that we catch
    # the error at the psyGen level too.
    monkeypatch.setattr(inline_trans, "validate", lambda node, inline: None)
    with pytest.raises(NotImplementedError) as err:
        _, _ = inline_trans.apply(kernels[1])
    assert "Cannot module-inline a transformed kernel " in str(err)