def test_constructor(): '''Test the optional constructor parameter (single node and list of nodes).''' from psyclone.tests.utilities import create_schedule code = '''module test contains subroutine tmp() integer :: a,b,c a = b/c c = a*b end subroutine tmp end module test''' schedule = create_schedule(code, "tmp") node1 = schedule[0] node2 = schedule[1] vai1 = VariablesAccessInfo(node1) assert str(vai1) == "a: WRITE, b: READ, c: READ" vai2 = VariablesAccessInfo([node1, node2]) assert str(vai2) == "a: READ+WRITE, b: READ, c: READ+WRITE" with pytest.raises(InternalError) as err: VariablesAccessInfo([node1, node2, 3]) assert "One element in the node list is not a Node, but of type " in \ str(err.value) # The error message is slightly different between python 2 and 3 # so only test for the part that is the same in both: assert "'int'>" in str(err.value) with pytest.raises(InternalError) as err: VariablesAccessInfo(1) assert "Error in VariablesAccessInfo" in str(err.value) # The error message is slightly different between python 2 and 3 # so only test for the part that is the same in both: assert "'int'>" in str(err.value)
def test_fw_exception(fort_writer): '''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, "tmp") # 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[0] binary_operation = assignment.rhs # The assignment.rhs method has no setter so access the reference # directly instead via children. assignment.children[1] = unsupported unsupported.children = binary_operation.children # Generate Fortran from the PSyIR schedule with pytest.raises(VisitorError) as excinfo: _ = fort_writer(schedule) assert "Unsupported node 'Unsupported' found" in str(excinfo)
def test_cw_loop(): '''Tests writing out a Loop node in C. It parses Fortran code and outputs it as C. Note that this is atm a literal translation, the loops are not functionally identical to Fortran, see TODO #523. ''' from psyclone.tests.utilities import create_schedule # Generate PSyIR from Fortran code. code = ''' module test contains subroutine tmp() integer :: i, a integer, dimension(:) :: b do i = 1, 20, 2 a = 2 * i enddo end subroutine tmp end module test''' schedule = create_schedule(code, "tmp") cvisitor = CWriter() result = cvisitor(schedule[0]) correct = '''for(i=1; i<=20; i+=2) { a = (2 * i); }''' result = cvisitor(schedule[0]) assert correct in result
def test_fw_ifblock(fort_writer): '''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, "tmp") # Generate Fortran from the PSyIR schedule result = fort_writer(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
def test_nemo_omp_parallel(): '''Tests if an OpenMP parallel 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''' schedule = create_schedule(code, "tmp") from psyclone.transformations import OMPParallelTrans # Now apply a parallel transform omp_par = OMPParallelTrans() # Note that the loop is not handled as nemo kernel, so the # omp node-type-check will find the assignment statement and # prevent application of omp parallel to the loop. So # disable the node type check so that omp parallel is applied. omp_par.apply(schedule[0], {"node-type-check": False}) fvisitor = FortranWriter() result = fvisitor(schedule) correct = '''!$omp parallel private(a,i) do i = 1, 20, 2 a=2 * i b(i)=b(i) + a enddo !$omp end parallel''' assert correct in result cvisitor = CWriter() result = cvisitor(schedule[0]) correct = '''#pragma omp parallel private(a,i) { for(i=1; i<=20; i+=2) { a = (2 * i); b[i] = (b[i] + a); } }''' result = cvisitor(schedule[0]) assert correct in result
def test_fw_size(fort_writer): ''' Check that the FortranWriter outputs a SIZE intrinsic call. ''' code = ("module test_mod\n" "contains\n" "subroutine test_kern(a)\n" " real, intent(in) :: a(:,:)\n" " integer :: mysize\n" " mysize = size(a, 2)\n" "end subroutine test_kern\n" "end module test_mod\n") schedule = create_schedule(code, "test_kern") # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) assert "mysize=size(a, 2)" in result.lower()
def test_fw_return(fort_writer): '''Check the FortranWriter class return method correctly prints out the Fortran representation. ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "contains\n" "subroutine tmp()\n" " return\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) assert " return\n" in result
def test_nemo_omp_parallel(): '''Tests if an OpenMP parallel 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''' schedule = create_schedule(code, "tmp") from psyclone.transformations import OMPParallelTrans # Now apply a parallel transform omp_par = OMPParallelTrans() omp_par.apply(schedule[0]) fvisitor = FortranWriter() result = fvisitor(schedule) correct = '''!$omp parallel private(a,i) do i = 1, 20, 2 a=2 * i b(i)=b(i) + a enddo !$omp end parallel''' assert correct in result cvisitor = CWriter() result = cvisitor(schedule[0]) correct = '''#pragma omp parallel private(a,i) { for(i=1; i<=20; i+=2) { a = (2 * i); b[i] = (b[i] + a); } }''' result = cvisitor(schedule[0]) assert correct in result
def test_fw_array(fort_writer): '''Check the FortranWriter class array method correctly prints out the Fortran representation of an array ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "contains\n" "subroutine tmp(a,n)\n" " integer, intent(in) :: n\n" " real, intent(out) :: a(n,n,n)\n" " a(2,n,3) = 0.0\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) assert "a(2,n,3)=0.0" in result
def test_fw_reference(fort_writer): '''Check the FortranWriter class reference method prints the appropriate information (the name of the reference it points to). Also check the method raises an exception if it has children as this is not expected. ''' # Generate fparser2 parse tree from Fortran code. The line of # interest is a(n) = 0.0. The additional a=1 line is added to get # round a bug in the parser. code = ("module test\n" "contains\n" "subroutine tmp(a,n)\n" " integer, intent(in) :: n\n" " real, intent(out) :: a(n)\n" " a = 1\n" " a(n) = 0.0\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) # The asserts need to be split as the declaration order can change # between different versions of Python. assert ("subroutine tmp(a,n)\n" " integer, intent(in) :: n\n" " real, dimension(n), intent(out) :: a\n" "\n" " a=1\n" " a(n)=0.0\n" "\n" "end subroutine tmp\n") in result # Now add a child to the reference node reference = schedule[1].lhs.children[0] reference.children = ["hello"] # Generate Fortran from the PSyIR schedule with pytest.raises(VisitorError) as excinfo: result = fort_writer(schedule) assert "Expecting a Reference with no children but found" in str(excinfo)
def test_fw_naryopeator(fort_writer): ''' Check that the FortranWriter class nary_operation method correctly prints out the Fortran representation of an intrinsic. ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "contains\n" "subroutine tmp(a,n)\n" " integer, intent(in) :: n\n" " real, intent(out) :: a\n" " a = max(1.0,1.0,2.0)\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) assert "a=MAX(1.0, 1.0, 2.0)" in result
def test_fw_unaryoperator(fort_writer): '''Check the FortranWriter class unary_operation method correctly prints out the Fortran representation. Uses -1 as the example. ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "contains\n" "subroutine tmp(a,n)\n" " integer, intent(in) :: n\n" " real, intent(out) :: a(n)\n" " a = -1\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) assert "a=-1" in result
def test_fw_loop(fort_writer): '''Check the FortranWriter class loop method correctly prints out the Fortran representation. ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "contains\n" "subroutine tmp()\n" " integer :: i, sum\n" " sum = 0\n" " do i = 1, 20, 2\n" " sum = sum + i\n" " end do\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) assert "do i = 1, 20, 2\n" in result
def test_fw_naryopeator_unknown(fort_writer, monkeypatch): ''' Check that the FortranWriter class nary_operation method raises the expected error if it encounters an unknown operator. ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "contains\n" "subroutine tmp(a,n)\n" " integer, intent(in) :: n\n" " real, intent(out) :: a\n" " a = max(1.0,1.0,2.0)\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Remove max() from the list of supported nary operators monkeypatch.delitem(Fparser2Reader.nary_operators, "max") # Generate Fortran from the PSyIR schedule with pytest.raises(VisitorError) as err: _ = fort_writer(schedule) assert "Unexpected N-ary op" in str(err)
def test_fw_unaryoperator_unknown(fort_writer, monkeypatch): '''Check the FortranWriter class unary_operation method raises an exception if an unknown unary operator is found. ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "contains\n" "subroutine tmp(a,n)\n" " integer, intent(in) :: n\n" " real, intent(out) :: a(n)\n" " a = sin(1.0)\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Remove sin() from the dict of unary operators monkeypatch.delitem(Fparser2Reader.unary_operators, "sin") # Generate Fortran from the PSyIR schedule with pytest.raises(VisitorError) as excinfo: _ = fort_writer(schedule) assert "Unexpected unary op" in str(excinfo)
def test_fw_container_3(fort_writer, monkeypatch): '''Check the FortranWriter class raises an exception when a Container node contains a symbol table with an argument declaration (as this does not make sense). ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "real :: a\n" "contains\n" "subroutine tmp()\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") container = schedule.root symbol = container.symbol_table.symbols[0] assert symbol.name == "a" monkeypatch.setattr(symbol, "_interface", Symbol.Argument()) with pytest.raises(VisitorError) as excinfo: _ = fort_writer(container) assert ("Arguments are not allowed in this context but this symbol table " "contains argument(s): '['a']'." in str(excinfo))
def test_fw_codeblock_1(fort_writer): '''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" " print *,\"I am a code block\"\n" " print *,\"with more than one line\"\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Check a code block exists in the schedule assert schedule.walk(CodeBlock) # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) assert (" a=1\n" " PRINT *, \"I am a code block\"\n" " PRINT *, \"with more than one line\"\n" in result)
def test_fw_container_2(fort_writer): '''Check the FortranWriter class outputs correct code when a Container node is found with a subroutine, use statements and declarations. Also raise an exception if the Container contains a Container. ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "use test2_mod, only : a,b\n" "real :: c,d\n" "contains\n" "subroutine tmp()\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") container = schedule.root # Generate Fortran from the PSyIR schedule result = fort_writer(container) assert ("module test\n" " use test2_mod, only : a\n" " use test2_mod, only : b\n" " real :: c\n" " real :: d\n\n" " contains\n" " subroutine tmp()\n\n\n" " end subroutine tmp\n\n" "end module test\n" in result) container.children.append(Container("child", parent=container)) with pytest.raises(VisitorError) as excinfo: _ = fort_writer(container) assert ("The Fortran back-end requires all children of a Container " "to be KernelSchedules." in str(excinfo))
def test_fw_kernelschedule(fort_writer, monkeypatch): '''Check the FortranWriter class outputs correct code when a KernelSchedule node is found. Also tests that an exception is raised if KernelSchedule.name does not have a value. ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "contains\n" "subroutine tmp(a,b,c)\n" " use my_mod, only : d\n" " real, intent(out) :: a(:)\n" " real, intent(in) :: b(:)\n" " integer, intent(in) :: c\n" " a = b/c\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) assert ("subroutine tmp(a,b,c)\n" " use my_mod, only : d\n" " real, dimension(:), intent(out) :: a\n" " real, dimension(:), intent(in) :: b\n" " integer, intent(in) :: c\n" "\n" " a=b / c\n" "\n" "end subroutine tmp\n") in result monkeypatch.setattr(schedule, "_name", None) with pytest.raises(VisitorError) as excinfo: _ = fort_writer(schedule) assert "Expected node name to have a value." in str(excinfo)
def test_fw_codeblock_2(fort_writer): '''Check the FortranWriter class codeblock method correctly prints out the Fortran representation when there is a code block that is part of a line (not a whole line). In this case the ":" in the array access is a code block. ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "contains\n" "subroutine tmp(a,n)\n" " integer, intent(in) :: n\n" " real, intent(out) :: a(n,n,n)\n" " a(2,n,:) = 0.0\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") # Check a code block exists in the schedule assert schedule.walk(CodeBlock) # Generate Fortran from the PSyIR schedule result = fort_writer(schedule) assert "a(2,n,:)=0.0" in result