예제 #1
0
def test_type_convert_binaryop_create(operation, op_str):
    '''Test that the create method in the BinaryOperation class correctly
    creates a BinaryOperation instance for the REAL and INT type-conversion
    operations..

    '''
    sym = DataSymbol("tmp1", REAL_SINGLE_TYPE)
    lhs = Reference(sym)
    wp_sym = DataSymbol("wp", INTEGER_SINGLE_TYPE)
    # Reference to a kind parameter
    rhs = Reference(wp_sym)
    binaryoperation = BinaryOperation.create(operation, lhs, rhs)
    assert binaryoperation._operator is operation
    check_links(binaryoperation, [lhs, rhs])
    result = FortranWriter().binaryoperation_node(binaryoperation)
    assert op_str + "(tmp1, wp)" in result.lower()
    # Kind specified with an integer literal
    rhs = Literal("4", INTEGER_SINGLE_TYPE)
    binaryoperation = BinaryOperation.create(operation, lhs.detach(), rhs)
    check_links(binaryoperation, [lhs, rhs])
    result = FortranWriter().binaryoperation_node(binaryoperation)
    assert op_str + "(tmp1, 4)" in result.lower()
    # Kind specified as an arithmetic expression
    rhs = BinaryOperation.create(BinaryOperation.Operator.ADD,
                                 Reference(wp_sym),
                                 Literal("2", INTEGER_SINGLE_TYPE))
    binaryoperation = BinaryOperation.create(operation, lhs.detach(), rhs)
    check_links(binaryoperation, [lhs, rhs])
    result = FortranWriter().binaryoperation_node(binaryoperation)
    assert op_str + "(tmp1, wp + 2)" in result.lower()
예제 #2
0
def test_ifblock_create():
    '''Test that the create method in an IfBlock class correctly creates
    an IfBlock instance.

    '''
    # Without an else clause.
    if_condition = Literal('true', BOOLEAN_TYPE)
    if_body = [Assignment.create(
        Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)),
        Literal("0.0", REAL_SINGLE_TYPE)),
               Assignment.create(
                   Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)),
                   Literal("1.0", REAL_SINGLE_TYPE))]
    ifblock = IfBlock.create(if_condition, if_body)
    if_schedule = ifblock.children[1]
    assert isinstance(if_schedule, Schedule)
    check_links(ifblock, [if_condition, if_schedule])
    check_links(if_schedule, if_body)
    result = FortranWriter().ifblock_node(ifblock)
    assert result == ("if (.true.) then\n"
                      "  tmp = 0.0\n"
                      "  tmp2 = 1.0\n"
                      "end if\n")

    # With an else clause.
    if_condition = Literal('true', BOOLEAN_TYPE)
    if_body = [Assignment.create(
        Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)),
        Literal("0.0", REAL_SINGLE_TYPE)),
               Assignment.create(
                   Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)),
                   Literal("1.0", REAL_SINGLE_TYPE))]
    else_body = [Assignment.create(Reference(DataSymbol("tmp",
                                                        REAL_SINGLE_TYPE)),
                                   Literal("1.0", REAL_SINGLE_TYPE)),
                 Assignment.create(Reference(DataSymbol("tmp2",
                                                        REAL_SINGLE_TYPE)),
                                   Literal("0.0", REAL_SINGLE_TYPE))]
    ifblock = IfBlock.create(if_condition, if_body, else_body)
    if_schedule = ifblock.children[1]
    assert isinstance(if_schedule, Schedule)
    else_schedule = ifblock.children[2]
    assert isinstance(else_schedule, Schedule)
    check_links(ifblock, [if_condition, if_schedule, else_schedule])
    check_links(if_schedule, if_body)
    check_links(else_schedule, else_body)
    result = FortranWriter().ifblock_node(ifblock)
    assert result == ("if (.true.) then\n"
                      "  tmp = 0.0\n"
                      "  tmp2 = 1.0\n"
                      "else\n"
                      "  tmp = 1.0\n"
                      "  tmp2 = 0.0\n"
                      "end if\n")
예제 #3
0
def test_nemo_omp_do(fortran_reader):
    '''Tests if an OpenMP do directive in NEMO is handled correctly.
    '''
    # Generate fparser2 parse tree from Fortran code.
    code = '''
        module test
        contains
        subroutine tmp()
          integer :: i, a
          integer, dimension(:) :: b
          do i = 1, 20, 2
            a = 2 * i
            b(i) = b(i) + a
          enddo
        end subroutine tmp
        end module test'''
    psyir = fortran_reader.psyir_from_source(code)
    schedule = psyir.children[0].children[0]

    # Now apply a parallel transform
    omp_loop = OMPLoopTrans()
    omp_loop.apply(schedule[0])
    # By default the visitor should raise an exception because the loop
    # directive is not inside a parallel region
    fvisitor_with_checks = FortranWriter()
    with pytest.raises(GenerationError) as err:
        fvisitor_with_checks(schedule)
    assert ("OMPDoDirective must be inside an OMP parallel region but could "
            "not find an ancestor OMPParallelDirective" in str(err.value))
    # Disable checks on global constraints to remove need for parallel region
    fvisitor = FortranWriter(check_global_constraints=False)
    result = fvisitor(schedule)
    correct = '''  !$omp do schedule(static)
  do i = 1, 20, 2
    a = 2 * i
    b(i) = b(i) + a
  enddo
  !$omp end do'''
    assert correct in result

    cvisitor = CWriter(check_global_constraints=False)
    result = cvisitor(schedule[0])
    correct = '''#pragma omp do schedule(static)
{
  for(i=1; i<=20; i+=2)
  {
    a = (2 * i);
    b[i] = (b[i] + a);
  }
}'''
    assert correct in result
예제 #4
0
def test_nemo_acc_kernels(default_present, expected, parser):
    '''
    Tests that an OpenACC kernels directive is handled correctly in the
    NEMO API.
    '''
    # Generate fparser2 parse tree from Fortran code.
    reader = FortranStringReader(NEMO_TEST_CODE)
    code = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(code)
    nemo_sched = psy.invokes.invoke_list[0].schedule

    # Now apply a kernels transform
    ktrans = ACCKernelsTrans()
    options = {"default_present": default_present}
    ktrans.apply(nemo_sched[0], options)

    fvisitor = FortranWriter()
    result = fvisitor(nemo_sched)
    correct = '''!$acc kernels{0}
do i = 1, 20, 2
  a = 2 * i + d(i)
  c(i) = a
  b(i) = b(i) + a + c(i)
enddo
!$acc end kernels'''.format(expected)
    assert correct in result

    cvisitor = CWriter()
    with pytest.raises(VisitorError) as err:
        _ = cvisitor(nemo_sched[0])
    assert "Unsupported node 'ACCKernelsDirective' found" in str(err.value)
예제 #5
0
def test_fw_codeblock():
    '''Check the FortranWriter class codeblock method correctly
    prints out the Fortran code contained within it.

    '''
    # Generate fparser2 parse tree from Fortran code.
    code = (
        "module test\n"
        "contains\n"
        "subroutine tmp()\n"
        "  integer :: a\n"
        "  a=1\n"
        "end subroutine tmp\n"
        "end module test")
    schedule = create_schedule(code)

    code1 = (
        "print *, 'I am a code block'\n"
        "print *, 'with more than one line'\n")
    _ = ParserFactory().create(std="f2003")
    reader = get_reader(code1)
    statements = Fortran2003.Execution_Part(reader)
    code_block = CodeBlock([statements], parent=schedule)
    schedule.addchild(code_block)

    # Generate Fortran from the PSyIR schedule
    fvisitor = FortranWriter()
    result = fvisitor(schedule)

    assert (
        "    a=1\n"
        "PRINT *, 'I am a code block'\n"
        "    PRINT *, 'with more than one line'\n" in result)
예제 #6
0
def test_correct_binary(func, output, tmpdir):
    '''Check that a valid example produces the expected output when the
    first argument to MIN is a simple argument and when it is an
    expression.

    '''
    Config.get().api = "nemo"
    operation = example_psyir_binary(func)
    writer = FortranWriter()
    result = writer(operation.root)
    assert ("subroutine min_example(arg,arg_1)\n"
            "  real, intent(inout) :: arg\n"
            "  real, intent(inout) :: arg_1\n"
            "  real :: psyir_tmp\n\n"
            "  psyir_tmp=MIN({0}, arg_1)\n\n"
            "end subroutine min_example\n".format(output)) in result
    trans = Min2CodeTrans()
    trans.apply(operation, operation.root.symbol_table)
    result = writer(operation.root)
    assert ("subroutine min_example(arg,arg_1)\n"
            "  real, intent(inout) :: arg\n"
            "  real, intent(inout) :: arg_1\n"
            "  real :: psyir_tmp\n"
            "  real :: res_min\n"
            "  real :: tmp_min\n\n"
            "  res_min={0}\n"
            "  tmp_min=arg_1\n"
            "  if (tmp_min < res_min) then\n"
            "    res_min=tmp_min\n"
            "  end if\n"
            "  psyir_tmp=res_min\n\n"
            "end subroutine min_example\n".format(output)) in result
    assert Compile(tmpdir).string_compiles(result)
    # Remove the created config instance
    Config._instance = None
예제 #7
0
def test_apply_reference_literal():
    '''Check that the apply method add bounds appropriately when the
    config file specifies a lower bound as a reference and an upper
    bound as a literal.

    '''
    _, invoke_info = get_invoke("implicit_many_dims.f90", api=API, idx=0)
    # Create a new config instance and load a test config file with
    # the bounds information set the way we want.
    config = Config.get(do_not_load_file=True)
    config.load(config_file=TEST_CONFIG)
    schedule = invoke_info.schedule
    assignment = schedule[0]
    array_ref = assignment.lhs
    trans = NemoArrayRange2LoopTrans()
    for index in range(4, -1, -1):
        range_node = array_ref.children[index]
        trans.apply(range_node)
    # Remove this config file so the next time the default one will be
    # loaded (in case we affect other tests)
    Config._instance = None
    writer = FortranWriter()
    result = writer(schedule)
    assert ("do idx = LBOUND(umask, 5), UBOUND(umask, 5), 1\n"
            "  do jt = 1, UBOUND(umask, 4), 1\n"
            "    do jk = jpk, 1, 1\n"
            "      do jj = 1, jpj, 1\n"
            "        do ji = jpi, 1, 1\n"
            "          umask(ji,jj,jk,jt,idx) = vmask(ji,jj,jk,jt,idx) + 1.0\n"
            "        enddo\n"
            "      enddo\n"
            "    enddo\n"
            "  enddo\n"
            "enddo" in result)
예제 #8
0
def test_gocean_omp_parallel():
    '''Test that an OMP PARALLEL directive in a 'classical' API (gocean here)
    is created correctly.
    '''

    from psyclone.transformations import OMPParallelTrans

    _, invoke = get_invoke("single_invoke.f90", "gocean1.0", idx=0)

    omp = OMPParallelTrans()
    omp_sched, _ = omp.apply(invoke.schedule[0])

    # Now remove the GOKern (since it's not yet supported in the
    # visitor pattern) and replace it with a simple assignment
    # TODO: #440 tracks this
    replace_child_with_assignment(omp_sched[0])

    # omp_sched is a GOInvokeSchedule, which is not yet supported.
    # So only convert starting from the OMPParallelDirective
    fvisitor = FortranWriter()
    result = fvisitor(omp_sched[0])
    correct = '''!$omp parallel
  a=b
!$omp end parallel'''
    assert correct in result

    cvisitor = CWriter()
    # Remove newlines for easier RE matching
    result = cvisitor(omp_sched[0])
    correct = '''#pragma omp parallel
{
  a = b;
}'''
    result = cvisitor(omp_sched[0])
    assert correct in result
예제 #9
0
def trans(psy):
    '''PSyclone transformation script for the Dynamo0.3 API to make the
    kernel values of ndofs, nlayers and nquadrature-point sizes constant.

    '''
    const_trans = Dynamo0p3KernelConstTrans()
    fortran_writer = FortranWriter()

    for invoke in psy.invokes.invoke_list:
        print("invoke '{0}'".format(invoke.name))
        schedule = invoke.schedule
        for kernel in schedule.coded_kernels():
            print("  kernel '{0}'".format(kernel.name.lower()))
            try:
                const_trans.apply(kernel, number_of_layers=NUMBER_OF_LAYERS,
                                  element_order=ELEMENT_ORDER,
                                  quadrature=CONSTANT_QUADRATURE)
            except TransformationError:
                print("    Failed to modify kernel '{0}'".format(kernel.name))

            kernel_schedule = kernel.get_kernel_schedule()
            kern_code = fortran_writer(kernel_schedule)
            print(kern_code)

    return psy
예제 #10
0
def test_program_handler(parser):
    '''Test that program handler works with multiple program units of
    different types.

    '''
    code = ("module a\n"
            "end module\n"
            "program b\n"
            "end program b\n"
            "subroutine c\n"
            "end subroutine")
    expected = ("module a\n"
                "  implicit none\n\n"
                "  contains\n\n"
                "end module a\n"
                "program b\n\n\n"
                "end program b\n"
                "subroutine c()\n\n\n"
                "end subroutine c\n")
    processor = Fparser2Reader()
    reader = FortranStringReader(code)
    parse_tree = parser(reader)
    psyir = processor._program_handler(parse_tree, None)
    # Check PSyIR nodes are being created
    assert isinstance(psyir, FileContainer)
    assert psyir.parent is None
    writer = FortranWriter()
    result = writer(psyir)
    assert result == expected
예제 #11
0
def test_fw_ifblock():
    '''Check the FortranWriter class ifblock method
    correctly prints out the Fortran representation.

    '''
    # Generate fparser2 parse tree from Fortran code.
    code = ("module test\n"
            "contains\n"
            "subroutine tmp(a,n)\n"
            "  integer, intent(inout) :: n\n"
            "  real, intent(out) :: a(n)\n"
            "    if (n.gt.2) then\n"
            "      n=n+1\n"
            "    end if\n"
            "    if (n.gt.4) then\n"
            "      a = -1\n"
            "    else\n"
            "      a = 1\n"
            "    end if\n"
            "end subroutine tmp\n"
            "end module test")
    schedule = create_schedule(code)

    # Generate Fortran from the PSyIR schedule
    fvisitor = FortranWriter()
    result = fvisitor(schedule)
    assert ("    if (n > 2) then\n"
            "      n=n + 1\n"
            "    end if\n"
            "    if (n > 4) then\n"
            "      a=-1\n"
            "    else\n"
            "      a=1\n"
            "    end if\n") in result
예제 #12
0
def test_fw_nemokern():
    '''Check the FortranWriter class nemokern method prints the
    class information and calls any children. This method is used to
    output nothing for a NemoKern object and simply call its children
    as NemoKern is a collection of PSyIR nodes so needs no
    output itself.

    '''
    # Generate fparser2 parse tree from Fortran code.
    code = ("module test\n"
            "contains\n"
            "subroutine tmp()\n"
            "  integer :: a,b,c\n"
            "  a = b/c\n"
            "end subroutine tmp\n"
            "end module test")
    schedule = create_schedule(code)

    # add a NemoKern object to the tree
    from psyclone.nemo import NemoKern
    nemo_kern = NemoKern(schedule.children, None, parent=schedule)
    schedule.children = [nemo_kern]

    # Generate Fortran from the PSyIR schedule
    fvisitor = FortranWriter()
    result = fvisitor(schedule)
    assert ("  subroutine tmp()\n"
            "    integer(i_def) :: a\n"
            "    integer(i_def) :: b\n"
            "    integer(i_def) :: c\n"
            "\n"
            "    a=b / c\n"
            "\n"
            "  end subroutine tmp\n") in result
예제 #13
0
def test_acc_data_region(parser):
    ''' Test that an ACCDataDirective node generates the expected code. '''
    # Generate fparser2 parse tree from Fortran code.
    reader = FortranStringReader(NEMO_TEST_CODE)
    code = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(code)
    sched = psy.invokes.invoke_list[0].schedule
    dtrans = ACCDataTrans()
    dtrans.apply(sched)
    fvisitor = FortranWriter()
    result = fvisitor(sched)
    assert ("!$acc data copyin(d) copyout(c) copy(b)\n"
            "do i = 1, 20, 2\n" in result)
    assert ("enddo\n"
            "!$acc end data\n" in result)
    assigns = sched.walk(Assignment)
    # Remove the read from array 'd'
    assigns[0].detach()
    result = fvisitor(sched)
    assert ("!$acc data copyout(c) copy(b)\n"
            "do i = 1, 20, 2\n" in result)
    # Remove the readwrite of array 'b'
    assigns[2].detach()
    result = fvisitor(sched)
    assert ("!$acc data copyout(c)\n"
            "do i = 1, 20, 2\n" in result)
예제 #14
0
def test_gocean_acc_parallel():
    '''Test that an ACC PARALLEL directive in a 'classical' API (gocean here)
    is created correctly.

    '''
    _, invoke = get_invoke("single_invoke.f90", "gocean1.0",
                           idx=0, dist_mem=False)

    ptrans = ACCParallelTrans()
    ptrans.apply(invoke.schedule[0])

    # Now remove the GOKern (since it's not yet supported in the
    # visitor pattern) and replace it with a simple assignment
    # TODO: #440 tracks this
    replace_child_with_assignment(invoke.schedule[0].dir_body)

    # omp_sched is a GOInvokeSchedule, which is not yet supported.
    # So only convert starting from the OMPParallelDirective. Also, disable
    # node validation so as to avoid the need for a data region.
    fvisitor = FortranWriter(check_global_constraints=False)
    result = fvisitor(invoke.schedule[0])
    correct = '''!$acc begin parallel default(present)
a = b
!$acc end parallel'''
    assert correct in result

    cvisitor = CWriter(check_global_constraints=False)
    with pytest.raises(VisitorError) as err:
        _ = cvisitor(invoke.schedule[0])
    assert "Unsupported node 'ACCParallelDirective' found" in str(err.value)
예제 #15
0
def test_nemo_acc_parallel(parser):
    '''Tests that an OpenACC parallel directive in NEMO is handled correctly.
    '''
    # Generate fparser2 parse tree from Fortran code.
    reader = FortranStringReader(NEMO_TEST_CODE)
    code = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(code)
    nemo_sched = psy.invokes.invoke_list[0].schedule

    # Now apply an ACC parallel transform
    dtrans = ACCDataTrans()
    ktrans = ACCParallelTrans()

    ktrans.apply(nemo_sched[0])
    dtrans.apply(nemo_sched[0])

    # Disable node validation to avoid having to add a data region
    fort_writer = FortranWriter(check_global_constraints=False)
    result = fort_writer(nemo_sched)

    correct = '''!$acc begin parallel default(present)
do i = 1, 20, 2
  a = 2 * i + d(i)
  c(i) = a
  b(i) = b(i) + a + c(i)
enddo
!$acc end parallel'''
    assert correct in result

    cvisitor = CWriter(check_global_constraints=False)
    with pytest.raises(VisitorError) as err:
        _ = cvisitor(nemo_sched[0])
    assert "Unsupported node 'ACCDataDirective' found" in str(err.value)
예제 #16
0
def trans(psy):
    '''PSyclone transformation script for the Dynamo0.3 API to optimise
    the matvec kernel for many-core CPUs. For the moment simply find
    the first matvec kernel in the example, transform the matmul
    intrinsic to equivalant inline code and then print out its PSyIR
    representation and output it as Fortran using the PSyIR Fortran
    back-end.

    '''
    matmul2code_trans = Matmul2CodeTrans()
    fortran_writer = FortranWriter()

    for invoke in psy.invokes.invoke_list:
        schedule = invoke.schedule
        for kernel in schedule.coded_kernels():
            if kernel.name.lower() == "matrix_vector_kernel_code":
                kernel_schedule = kernel.get_kernel_schedule()
                # Replace matmul with inline code
                for bin_op in kernel_schedule.walk(BinaryOperation):
                    if bin_op.operator is BinaryOperation.Operator.MATMUL:
                        matmul2code_trans.apply(bin_op)
                # Future optimisations will go here.
                kernel_schedule.view()
                result = fortran_writer(kernel_schedule)
                print(result)
                # Abort after the first matrix vector kernel for the
                # time being.
                print("Aborting to view the modifications to the matrix "
                      "vector kernel")
                sys.exit()
    return psy
예제 #17
0
def test_apply_multi_assignments():
    '''Check that the apply() method can be called for multiple assigments
    with and without ranges.

    '''
    _, invoke_info = get_invoke("array_syntax.f90", api=API, idx=0)
    schedule = invoke_info.schedule
    trans = NemoAllArrayRange2LoopTrans()
    for assignment in schedule.walk(Assignment):
        trans.apply(assignment)
    writer = FortranWriter()
    result = writer(schedule)
    expected = ("do jk = 1, jpk, 1\n"
                "  do jj = 1, jpj, 1\n"
                "    do ji = 1, jpi, 1\n"
                "      zftv(ji,jj,jk) = 0.0e0\n"
                "    enddo\n"
                "  enddo\n"
                "enddo\n"
                "if (l_ptr) then\n"
                "  call dia_ptr_hst(jn, ''ldf'', -zftv(:,:,:))\n"
                "end if\n"
                "call dia_ptr_hst(jn, ''ldf'', -zftv(:,:,:))\n"
                "do jj = 1, jpj, 1\n"
                "  do ji = 1, jpi, 1\n"
                "    zftu(ji,jj,1) = 1.0e0\n"
                "  enddo\n"
                "enddo\n"
                "do jj = 1, jpj, 1\n"
                "  do ji = 1, jpi, 1\n"
                "    tmask(ji,jj) = jpi\n"
                "  enddo\n"
                "enddo\n")
    assert expected in result
예제 #18
0
def test_transform_apply_mixed_implicit_do():
    '''Check that the PSyIR is transformed as expected for a lat,lon,levs
    loop with some of its indices accessed using array notation and
    some using explicit loops.  The resultant Fortran code is used to
    confirm the transformation has worked correctly.

    '''
    _, invoke_info = get_invoke("explicit_over_implicit.f90", api=API, idx=0)
    schedule = invoke_info.schedule
    assignment = schedule[0].loop_body[0]
    trans = NemoOuterArrayRange2LoopTrans()
    # Apply transformation 2 times, once for each implicit
    # dimension
    trans.apply(assignment)
    writer = FortranWriter()
    result = writer(schedule)
    expected = (
        "do jk = 1, jpk, 1\n"
        "  do jj = 1, jpj, 1\n"
        "    umask(:,jj,jk) = vmask(:,jj,jk) + 1.0\n"
        "  enddo\n"
        "enddo")
    assert expected in result
    trans.apply(assignment)
    result = writer(schedule)
    expected = (
        "do jk = 1, jpk, 1\n"
        "  do jj = 1, jpj, 1\n"
        "    do ji = 1, jpi, 1\n"
        "      umask(ji,jj,jk) = vmask(ji,jj,jk) + 1.0\n"
        "    enddo\n"
        "  enddo\n"
        "enddo")
    assert expected in result
예제 #19
0
def trans(psy):
    ''' Use the PSyIR back-end to generate PSy-layer target code'''

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

    print("DSL level view:")
    schedule.view()

    print("f2pygen code:")
    print(str(psy.gen))

    # In-place lowering to Language-level PSyIR
    schedule.lower_to_language_level()

    print("")
    print("Language level view:")
    schedule.view()

    fvisitor = FortranWriter()
    print("")
    print("FortranWriter code:")
    print(fvisitor(psy.container))

    # This PSyclone call should terminate gracefully here
    sys.exit(0)
예제 #20
0
def generate_adjoint_str(tl_fortran_str):
    '''Takes an LFRic tangent-linear kernel encoded as a string as input
    and returns its adjoint encoded as a string.

    :param str tl_fortran_str: Fortran implementation of an LFRic \
        tangent-linear kernel.

    :returns: a string containing the Fortran implementation of the \
        supplied tangent-linear kernel.
    :rtype: str

    '''
    logger = logging.getLogger(__name__)
    logger.debug(tl_fortran_str)

    # TL Language-level PSyIR
    reader = FortranReader()
    tl_psyir = reader.psyir_from_source(tl_fortran_str)

    # Addressing issue #1238 will allow the view() method to be output
    # to the logger.
    # logger.debug(tl_psyir.view())

    # TL to AD translation
    ad_psyir = generate_adjoint(tl_psyir)

    # AD Fortran code
    writer = FortranWriter()
    adjoint_fortran_str = writer(ad_psyir)
    logger.debug(adjoint_fortran_str)

    return adjoint_fortran_str
예제 #21
0
def test_apply2(tmpdir):
    '''Test that the matmul2code apply method produces the expected
    PSyIR. We use the Fortran backend to help provide the test for
    correctness. This example includes extra indices for the vector
    and matrix arrays with additional indices being literals.

    '''
    trans = Matmul2CodeTrans()
    matmul = create_matmul()
    matmul.children[0].children[2] = Literal("1", INTEGER_TYPE)
    matmul.children[1].children[1] = Literal("2", INTEGER_TYPE)
    trans.apply(matmul)
    writer = FortranWriter()
    result = writer(matmul.root)
    assert ("subroutine my_kern()\n"
            "  integer, parameter :: idx = 3\n"
            "  real, dimension(5,10,15) :: x\n"
            "  real, dimension(10,20) :: y\n"
            "  real, dimension(10) :: result\n"
            "  integer :: i\n"
            "  integer :: j\n"
            "\n"
            "  do i = 1, 5, 1\n"
            "    result(i)=0.0\n"
            "    do j = 1, 10, 1\n"
            "      result(i)=result(i) + x(i,j,1) * y(j,2)\n"
            "    enddo\n"
            "  enddo\n"
            "\n"
            "end subroutine my_kern" in result)
    assert Compile(tmpdir).string_compiles(result)
예제 #22
0
def test_transform_multi_apply(tmpdir):
    '''Check that the ArrayRange2Loop transformation can be used to create
    nested loops by calling it multiple times when an array has
    multiple dimensions that use a range.

    '''
    trans = ArrayRange2LoopTrans()

    symbol_table = SymbolTable()
    symbol = DataSymbol("n", INTEGER_TYPE)
    symbol_table.add(symbol)
    lhs = create_array_y_2d_slice(symbol_table)
    rhs = create_array_z(symbol_table)
    assignment = Assignment.create(lhs, rhs)
    routine = KernelSchedule.create("work", symbol_table, [assignment])
    trans.apply(assignment)
    trans.apply(assignment)
    expected = ("  do idx = LBOUND(y2, 2), UBOUND(y2, 2), 1\n"
                "    do idx_1 = LBOUND(y2, 1), UBOUND(y2, 1), 1\n"
                "      y2(idx_1,idx)=z(idx_1,n,idx)\n"
                "    enddo\n"
                "  enddo\n")
    writer = FortranWriter()
    result = writer(routine)
    assert expected in result
    assert Compile(tmpdir).string_compiles(result)
예제 #23
0
def test_create():
    '''Test that the static create() method creates a NemoLoop instance
    and its children corectly.

    '''
    variable = DataSymbol("ji", INTEGER_TYPE)
    start = Literal("2", INTEGER_TYPE)
    stop = Literal("10", INTEGER_TYPE)
    step = Literal("1", INTEGER_TYPE)
    x_var = DataSymbol("X", REAL_TYPE)
    children = [Assignment.create(Reference(x_var), Literal("3.0", REAL_TYPE))]
    nemo_loop = NemoLoop.create(variable, start, stop, step, children)
    assert isinstance(nemo_loop, NemoLoop)
    assert nemo_loop.loop_type == "lon"
    assert isinstance(nemo_loop.loop_body, Schedule)
    assert len(nemo_loop.loop_body.children) == 1
    assert nemo_loop.loop_body.children[0] is children[0]
    assert children[0].parent is nemo_loop.loop_body
    assert nemo_loop.loop_body.parent is nemo_loop
    assert nemo_loop.variable is variable
    assert nemo_loop.start_expr is start
    assert nemo_loop.stop_expr is stop
    assert nemo_loop.step_expr is step
    writer = FortranWriter()
    result = writer(nemo_loop)
    assert ("do ji = 2, 10, 1\n" "  X = 3.0\n" "enddo" in result)
예제 #24
0
def test_transform_apply_insert(tmpdir):
    '''Check that the PSyIR is transformed as expected when there are
    multiple statements in the PSyIR. The resultant Fortran code is used to
    confirm the transformation has worked correctly.

    '''
    trans = ArrayRange2LoopTrans()

    symbol_table = SymbolTable()
    symbol = DataSymbol("n", INTEGER_TYPE)
    symbol_table.add(symbol)
    # Create the first assignment. In Fortran notation: x(:) = y(n,:)
    lhs = create_array_x(symbol_table)
    rhs = create_array_y(symbol_table)
    assignment1 = Assignment.create(lhs, rhs)
    # Create the second assignment. In Fortran notation: y2(:,:) = z(:,n,:)
    lhs = create_array_y_2d_slice(symbol_table)
    rhs = create_array_z(symbol_table)
    assignment2 = Assignment.create(lhs, rhs)
    routine = KernelSchedule.create("work", symbol_table,
                                    [assignment1, assignment2])
    trans.apply(assignment1)
    trans.apply(assignment2)
    writer = FortranWriter()
    expected = ("  do idx = LBOUND(x, 1), UBOUND(x, 1), 1\n"
                "    x(idx)=y(n,idx)\n"
                "  enddo\n"
                "  do idx_1 = LBOUND(y2, 2), UBOUND(y2, 2), 1\n"
                "    y2(:,idx_1)=z(:,n,idx_1)\n"
                "  enddo\n")
    result = writer(routine)
    assert expected in result
    assert Compile(tmpdir).string_compiles(result)
예제 #25
0
def test_apply_existing_names():
    '''Check that the apply method uses existing iterators appropriately
    when their symbols are already defined.

    '''
    _, invoke_info = get_invoke("implicit_do.f90", api=API, idx=0)
    schedule = invoke_info.schedule
    assignment = schedule[0]
    array_ref = assignment.lhs
    trans = NemoArrayRange2LoopTrans()
    symbol_table = schedule.symbol_table
    symbol_table.add(DataSymbol("ji", INTEGER_TYPE))
    symbol_table.add(DataSymbol("jj", INTEGER_TYPE))
    symbol_table.add(DataSymbol("jk", INTEGER_TYPE))

    trans.apply(array_ref.children[2])
    trans.apply(array_ref.children[1])
    trans.apply(array_ref.children[0])

    writer = FortranWriter()
    result = writer(schedule)
    assert ("do jk = 1, jpk, 1\n"
            "  do jj = 1, jpj, 1\n"
            "    do ji = 1, jpi, 1\n"
            "      umask(ji,jj,jk) = 0.0e0\n"
            "    enddo\n"
            "  enddo\n"
            "enddo" in result)
예제 #26
0
def trans(psy):
    ''' Use the PSyIR back-end to generate PSy-layer target code'''

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

        print("Transforming invoke '" + invoke.name + "'...")
        schedule = invoke.schedule

        print("DSL level view:")
        schedule.view()

    print("f2pygen code:")
    print(str(psy.gen))

    # TODO #1010: This script should terminate here until LFRic declares
    # all its symbols to the symbol table.
    sys.exit(0)

    for invoke in psy.invokes.invoke_list:
        # In-place lowering to Language-level PSyIR
        schedule.symbol_table.view()
        schedule.lower_to_language_level()

        print("")
        print("Language level view:")
        schedule.view()

    print("")
    print("FortranWriter code:")
    fvisitor = FortranWriter()
    print(fvisitor(psy.container))
예제 #27
0
def test_fw_exception():
    '''Check the FortranWriter class instance raises an exception if an
    unsupported PSyIR node is found.

    '''
    # Generate fparser2 parse tree from Fortran code.
    code = (
        "module test\n"
        "contains\n"
        "subroutine tmp()\n"
        "  integer :: a,b,c\n"
        "  a = b/c\n"
        "end subroutine tmp\n"
        "end module test")
    schedule = create_schedule(code)

    # pylint: disable=abstract-method
    # modify the reference to b to be something unsupported
    class Unsupported(Node):
        '''A PSyIR node that will not be supported by the Fortran visitor.'''
    # pylint: enable=abstract-method

    unsupported = Unsupported()
    assignment = schedule.children[0]
    binary_operation = assignment.children[1]
    assignment.children[1] = unsupported
    unsupported.children = binary_operation.children

    # Generate Fortran from the PSyIR schedule
    fvisitor = FortranWriter()
    with pytest.raises(VisitorError) as excinfo:
        _ = fvisitor(schedule)
    assert "Unsupported node 'Unsupported' found" in str(excinfo)
예제 #28
0
def test_correct(func, output, tmpdir):
    '''Check that a valid example produces the expected output when the
    argument to ABS is a simple argument and when it is an
    expresssion.

    '''
    Config.get().api = "nemo"
    operation = example_psyir(func)
    writer = FortranWriter()
    result = writer(operation.root)
    assert ("subroutine abs_example(arg)\n"
            "  real, intent(inout) :: arg\n"
            "  real :: psyir_tmp\n\n"
            "  psyir_tmp = ABS({0})\n\n"
            "end subroutine abs_example\n".format(output)) in result
    trans = Abs2CodeTrans()
    trans.apply(operation, operation.root.symbol_table)
    result = writer(operation.root)
    assert ("subroutine abs_example(arg)\n"
            "  real, intent(inout) :: arg\n"
            "  real :: psyir_tmp\n"
            "  real :: res_abs\n"
            "  real :: tmp_abs\n\n"
            "  tmp_abs = {0}\n"
            "  if (tmp_abs > 0.0) then\n"
            "    res_abs = tmp_abs\n"
            "  else\n"
            "    res_abs = tmp_abs * -1.0\n"
            "  end if\n"
            "  psyir_tmp = res_abs\n\n"
            "end subroutine abs_example\n".format(output)) in result
    assert Compile(tmpdir).string_compiles(result)
    # Remove the created config instance
    Config._instance = None
예제 #29
0
def test_acc_data_region_no_struct(parser):
    '''
    Test that we refuse to generate code if a data region includes references
    to structures.
    '''
    reader = FortranStringReader('''
module test
  use some_mod, only: grid_type
  type(grid_type) :: grid
contains
subroutine tmp()
  integer :: i
  integer, dimension(:) :: b

  do i = 1, 20, 2
    b(i) = b(i) + i + grid%flag
    grid%data(i) = i
  enddo
end subroutine tmp
end module test''')
    code = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(code)
    sched = psy.invokes.invoke_list[0].schedule
    dtrans = ACCDataTrans()
    dtrans.apply(sched)
    fvisitor = FortranWriter()
    with pytest.raises(NotImplementedError) as err:
        _ = fvisitor(sched)
    # Allow for the vagaries of py2 output versus py3
    err_msg = str(err.value).replace("u'", "'")
    assert ("Structure (derived-type) references are not yet supported within "
            "OpenACC data regions but found: ['grid%flag', 'grid%data(i)']" in
            err_msg)
예제 #30
0
    def gen_code(self, parent):
        '''
        Generate the Fortran Loop and any associated code.

        :param parent: the node in the f2pygen AST to which to add content.
        :type parent: :py:class:`psyclone.f2pygen.SubroutineGen`

        '''
        # Avoid circular dependency
        # pylint: disable=import-outside-toplevel
        from psyclone.psyGen import zero_reduction_variables, InvokeSchedule

        def is_unit_literal(expr):
            ''' Check if the given expression is equal to the literal '1'.

            :param expr: a PSyIR expression.
            :type expr: :py:class:`psyclone.psyir.nodes.Node`

            :returns: True if it is equal to the literal '1', false otherwise.
            '''
            return isinstance(expr, Literal) and expr.value == '1'

        if not self.is_openmp_parallel():
            calls = self.reductions()
            zero_reduction_variables(calls, parent)

        invoke = self.ancestor(InvokeSchedule)
        if invoke.opencl or (is_unit_literal(self.start_expr)
                             and is_unit_literal(self.stop_expr)):
            # no need for a loop
            for child in self.loop_body:
                child.gen_code(parent)
        else:
            # Avoid circular dependency
            # pylint: disable=import-outside-toplevel
            from psyclone.psyir.backend.fortran import FortranWriter
            # start/stop/step_expr are generated with the FortranWriter
            # backend, the rest of the loop with f2pygen.
            fwriter = FortranWriter()
            if is_unit_literal(self.step_expr):
                step_str = None
            else:
                step_str = fwriter(self.step_expr)

            do_stmt = DoGen(parent,
                            self.variable.name, fwriter(self.start_expr),
                            fwriter(self.stop_expr), step_str)
            # need to add do loop before children as children may want to add
            # info outside of do loop
            parent.add(do_stmt)
            for child in self.loop_body:
                child.gen_code(do_stmt)
            my_decl = DeclGen(parent,
                              datatype="integer",
                              entity_decls=[self.variable.name])
            parent.add(my_decl)