Пример #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 inline():
    ''' function exercising the module-inline transformation '''
    from psyclone.parse.algorithm import parse
    from psyclone.psyGen import PSyFactory
    import os
    from psyclone.transformations import KernelModuleInlineTrans

    _, info = parse(os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                 "..", "..", "..", "src", "psyclone", "tests",
                                 "test_files", "dynamo0p1", "algorithm",
                                 "1_single_function.f90"),
                    api="dynamo0.1")
    psy = PSyFactory("dynamo0.1").create(info)
    invokes = psy.invokes
    print(psy.invokes.names)
    invoke = invokes.get("invoke_0_testkern_type")
    schedule = invoke.schedule
    schedule.view()
    kern = schedule.children[0].loop_body[0]
    # setting module inline directly
    kern.module_inline = True
    schedule.view()
    # unsetting module inline via a transformation
    trans = KernelModuleInlineTrans()
    schedule, _ = trans.apply(kern, {"inline": False})
    schedule.view()
    # setting module inline via a transformation
    schedule, _ = trans.apply(kern)
    schedule.view()
    print(str(psy.gen))
Пример #3
0
def test_kernel_trans_validate(monkeypatch):
    '''Check that the validate method in the class KernelTrans raises an
    exception if the reference is not found in any of the symbol
    tables. KernelTrans can't be instantiated as it is abstract so use
    a the subclass.

    '''
    from psyclone.transformations import KernelModuleInlineTrans
    kernel_trans = KernelModuleInlineTrans()
    _, invoke = get_invoke("single_invoke_kern_with_global.f90",
                           api="gocean1.0",
                           idx=0)
    sched = invoke.schedule
    kernels = sched.walk(Kern)
    kernel = kernels[0]

    def dummy_func():
        '''Simple Dummy function that raises SymbolError.'''
        from psyclone.psyir.symbols import SymbolError
        raise SymbolError("error")

    monkeypatch.setattr(kernel, "get_kernel_schedule", dummy_func)
    with pytest.raises(TransformationError) as err:
        _, _ = kernel_trans.apply(kernel)
    assert ("'kernel_with_global_code' contains accesses to data that are "
            "not captured in the PSyIR Symbol Table(s) (error)."
            "" in str(err.value))
def test_transformation_inline_error_if_not_kernel():
    ''' Test that the inline transformation fails if the object being
    passed is not a kernel'''
    _, invoke = get_invoke("single_invoke_three_kernels.f90", 0)
    schedule = invoke.schedule
    kern_call = schedule.children[0].children[0]
    inline_trans = KernelModuleInlineTrans()
    with pytest.raises(TransformationError):
        _, _ = inline_trans.apply(kern_call)
def test_module_inline_warning_no_change():
    ''' test of the warning clause in the Kernel transformation when
    no change is made to the inlining of a Kernel i.e. the inlining
    request is already what is happening. No warning is currently made
    as we have not added logging to the code but this test covers the
    clause '''
    _, invoke = get_invoke("test14_module_inline_same_kernel.f90", 0)
    schedule = invoke.schedule
    kern_call = schedule.children[0].children[0].children[0]
    inline_trans = KernelModuleInlineTrans()
    _, _ = inline_trans.apply(kern_call, inline=False)
def test_module_inline_with_transformation():
    ''' Test that we can succesfully inline a basic kernel subroutine
    routine into the PSy layer module using a transformation '''
    psy, invoke = get_invoke("single_invoke_three_kernels.f90", 0)
    schedule = invoke.schedule
    kern_call = schedule.children[1].children[0].children[0]
    inline_trans = KernelModuleInlineTrans()
    schedule, _ = inline_trans.apply(kern_call)
    gen = str(psy.gen)
    # check that the subroutine has been inlined
    assert 'SUBROUTINE compute_cv_code(i, j, cv, p, v)' in gen
    # check that the associated use no longer exists
    assert 'USE compute_cv_mod, ONLY: compute_cv_code' not in gen
def test_module_inline_with_sub_use():
    ''' Test that we can module inline a kernel subroutine which
    contains a use statement'''
    psy, invoke = get_invoke("single_invoke_scalar_int_arg.f90", 0)
    schedule = invoke.schedule
    kern_call = schedule.children[0].children[0].children[0]
    inline_trans = KernelModuleInlineTrans()
    schedule, _ = inline_trans.apply(kern_call)
    gen = str(psy.gen)
    # check that the subroutine has been inlined
    assert 'SUBROUTINE bc_ssh_code(ji, jj, istep, ssha, tmask)' in gen
    # check that the use within the subroutine exists
    assert 'USE model_mod, ONLY: rdt' in gen
    # check that the associated psy use does not exist
    assert 'USE bc_ssh_mod, ONLY: bc_ssh_code' not in gen
Пример #8
0
def test_no_inline_global_var():
    ''' Check that we refuse to in-line a kernel that accesses a global
    variable. '''
    from psyclone.transformations import KernelModuleInlineTrans
    inline_trans = KernelModuleInlineTrans()
    _, invoke = get_invoke("single_invoke_kern_with_global.f90",
                           api="gocean1.0",
                           idx=0)
    sched = invoke.schedule
    kernels = sched.walk(Kern)
    with pytest.raises(TransformationError) as err:
        _, _ = inline_trans.apply(kernels[0])
    assert ("'kernel_with_global_code' contains accesses to data (variable "
            "'alpha') that are not captured in the PSyIR Symbol Table(s) "
            "within KernelSchedule scope." in str(err.value))
def test_module_inline_same_kernel():
    '''Tests that correct results are obtained when an invoke that uses
    the same kernel subroutine more than once has that kernel
    inlined'''
    psy, invoke = get_invoke("test14_module_inline_same_kernel.f90", 0)
    schedule = invoke.schedule
    kern_call = schedule.children[0].children[0].children[0]
    inline_trans = KernelModuleInlineTrans()
    _, _ = inline_trans.apply(kern_call)
    gen = str(psy.gen)
    # check that the subroutine has been inlined
    assert 'SUBROUTINE time_smooth_code(' in gen
    # check that the associated psy "use" does not exist
    assert 'USE time_smooth_mod, ONLY: time_smooth_code' not in gen
    # check that the subroutine has only been inlined once
    count = count_lines(psy.gen, "SUBROUTINE time_smooth_code(")
    assert count == 1, "Expecting subroutine to be inlined once"
def test_module_no_inline_with_transformation():
    ''' Test that we can switch off the inlining of a kernel routine
    into the PSy layer module using a transformation. Relies on the
    test_module_inline() test being successful to be a valid test. '''
    psy, invoke = get_invoke("single_invoke_three_kernels.f90", 0)
    schedule = invoke.schedule
    kern_call = schedule.children[0].children[0].children[0]
    # directly switch on inlining
    kern_call.module_inline = True
    inline_trans = KernelModuleInlineTrans()
    # use a transformation to switch inlining off again
    schedule, _ = inline_trans.apply(kern_call, inline=False)
    gen = str(psy.gen)
    # check that the subroutine has not been inlined
    assert 'SUBROUTINE compute_cu_code(i, j, cu, p, u)' not in gen
    # check that the associated use exists (as this is removed when
    # inlining)
    assert 'USE compute_cu_mod, ONLY: compute_cu_code' in gen
Пример #11
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
Пример #12
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)
Пример #13
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)
Пример #14
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
Пример #15
0
def trans(psy):
    '''Transformation entry point'''
    config = Config.get()

    schedule = psy.invokes.get('invoke_0').schedule

    loop_trans = OMPTaskloopTrans(grainsize=32, nogroup=True)
    wait_trans = OMPTaskwaitTrans()

    module_inline_trans = KernelModuleInlineTrans()

    # Inline all kernels in this Schedule
    for kernel in schedule.kernels():
        module_inline_trans.apply(kernel)

    for child in schedule.children:
        if isinstance(child, Loop):
            loop_trans.apply(child)

    single_trans = OMPSingleTrans()
    parallel_trans = OMPParallelTrans()
    sets = []
    if not config.distributed_memory:
        single_trans.apply(schedule.children)
        parallel_trans.apply(schedule.children)
        wait_trans.apply(schedule.children[0])
        return
    # Find all of the groupings of taskloop and taskwait directives. Each of
    # these groups needs its own parallel+single regions. This makes sure we
    # don't apply OpenMP transformations to the Halo Exchange operations.
    next_start = 0
    next_end = 0
    idx = 0
    for idx, child in enumerate(schedule.children):
        # Loop through through the schedule until we find a non-OpenMP
        # node, and extend the grouping until we do.
        if isinstance(child, (OMPTaskloopDirective, OMPTaskwaitDirective)):
            next_end = next_end + 1
        elif not isinstance(child, OMPDirective):
            # If we find a non OpenMP directive, if we're currently in a
            # grouping of OpenMP directives then we stop, and add it to
            # the set of groupings. Otherwise we just skip over this
            # node.
            if next_start == idx:
                next_end = idx + 1
                next_start = idx + 1
            else:
                sets.append((next_start, next_end))
                next_end = idx + 1
                next_start = idx + 1
        else:
            next_end = next_end + 1
    # If currently in a grouping of directives, add it to the list
    # of groupings
    if next_start <= idx:
        sets.append((next_start, idx + 1))
    # Start from the last grouping to keep indexing correct,
    # so reverse the ordering
    sets.reverse()
    # For each of the groupings of OpenMP directives, surround them
    # with an OpenMP Single and an OpenMP Parallel directive set.
    for next_set in sets:
        single_trans.apply(schedule[next_set[0]:next_set[1]])
        parallel_trans.apply(schedule[next_set[0]])
    # Finally, we loop over the OMPParallelDirectives, and apply the
    # OMPTaskWaitTrans to ensure correctness.
    for child in schedule.children:
        if isinstance(child, OMPParallelDirective):
            wait_trans.apply(child)