Skip to content
Merged
4 changes: 4 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
46) PR 3033 for #3031 and #3032. Prevent inserting profiling callipers to
NEMO functions and any elemental function. Also remove pure attribute when
inserting psy_data wrappers.

45) PR #2948 for #2950. Update extraction transformation to perform
extraction and driver creation at lowering

Expand Down
28 changes: 20 additions & 8 deletions examples/nemo/scripts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@

''' Utilities file to parallelise Nemo code. '''

from typing import List, Union

from psyclone.domain.common.transformations import KernelModuleInlineTrans
from psyclone.psyir.nodes import (
Assignment, Loop, Directive, Reference, CodeBlock, ArrayReference,
Call, Return, IfBlock, Routine, IntrinsicCall, StructureReference)
Assignment, Loop, Directive, Node, Reference, CodeBlock, ArrayReference,
Call, Return, IfBlock, Routine, Schedule, IntrinsicCall,
StructureReference)
from psyclone.psyir.symbols import (
DataSymbol, INTEGER_TYPE, ScalarType, RoutineSymbol)
from psyclone.psyir.transformations import (
Expand Down Expand Up @@ -501,19 +504,28 @@ def insert_explicit_loop_parallelism(
continue


def add_profiling(children):
def add_profiling(children: Union[List[Node], Schedule]):
'''
Walks down the PSyIR and inserts the largest possible profiling regions.
Code that contains directives is excluded.
Walks down the PSyIR and inserts the largest possible profiling regions
in place. Code inside functions or that contains directives is excluded.

:param children: sibling nodes in the PSyIR to which to attempt to add \
profiling regions.
:type children: list of :py:class:`psyclone.psyir.nodes.Node`
:param children: a Schedule or sibling nodes in the PSyIR to which to
attempt to add profiling regions.

'''
if children and isinstance(children, Schedule):
# If we are given a Schedule, we look at its children.
children = children.children

if not children:
return

# We do not want profiling calipers inside functions (such as the
# PSyclone-generated comparison functions).
parent_routine = children[0].ancestor(Routine)
if parent_routine and parent_routine.return_symbol:
return

node_list = []
for child in children[:]:
# Do we want this node to be included in a profiling region?
Expand Down
18 changes: 18 additions & 0 deletions src/psyclone/psyir/transformations/psy_data_trans.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ def validate(self, nodes, options=None):
:raises TransformationError: if there will be a name clash between \
any existing symbols and those that must be imported from the \
appropriate PSyData library.
:raises TransformationError: if the target nodes are within an
ELEMENTAL routine.

'''
# pylint: disable=too-many-branches
Expand Down Expand Up @@ -263,6 +265,13 @@ def validate(self, nodes, options=None):
except KeyError:
pass

parent_routine = node_list[0].ancestor(Routine)
if parent_routine and parent_routine.symbol.is_elemental:
raise TransformationError(
f"Cannot add PSyData calls inside ELEMENTAL routine "
f"'{parent_routine.symbol.name}' because it would change its "
f"semantics.")

super().validate(node_list, options)

# ------------------------------------------------------------------------
Expand All @@ -272,6 +281,9 @@ def apply(self, nodes, options=None):
schedule - i.e. enclose the specified Nodes in the
schedule within a single PSyData region.

Note that if the nodes are within a routine that previously had the
`pure` attribute, this attribute is removed.

:param nodes: can be a single node or a list of nodes.
:type nodes: :py:obj:`psyclone.psyir.nodes.Node` or list of \
:py:obj:`psyclone.psyir.nodes.Node`
Expand Down Expand Up @@ -314,6 +326,12 @@ def apply(self, nodes, options=None):
node_list, symbol_table=table, options=options)
parent.addchild(psy_data_node, position)

# If we've added PSyData calls to a pure routine then it is
# no longer pure.
parent_routine = node_list[0].ancestor(Routine)
if parent_routine and parent_routine.symbol.is_pure:
parent_routine.symbol.is_pure = False


# =============================================================================
# For AutoAPI documentation generation
Expand Down
65 changes: 60 additions & 5 deletions src/psyclone/tests/psyir/transformations/psy_data_trans_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,9 @@

from psyclone.configuration import Config
from psyclone.errors import InternalError
from psyclone.psyir.nodes import PSyDataNode
from psyclone.psyir.transformations import (PSyDataTrans,
ReadOnlyVerifyTrans,
TransformationError)
from psyclone.psyir.nodes import Assignment, Loop, PSyDataNode, Routine
from psyclone.psyir.transformations import (
OMPLoopTrans, PSyDataTrans, ReadOnlyVerifyTrans, TransformationError)
from psyclone.tests.utilities import get_invoke


Expand Down Expand Up @@ -85,7 +84,44 @@ def test_psy_data_trans_basic():
children[0] is node


# -----------------------------------------------------------------------------
def test_psy_data_trans_validate_not_inside_loop_directive(fortran_reader):
'''
Check that the transformation refuses to add caliper nodes between
a loop-directive and the associated loop.

'''
otrans = OMPLoopTrans()
psytrans = PSyDataTrans()
psyir = fortran_reader.psyir_from_source('''\
subroutine a_test()
integer :: i, va(10)
do i = 1, 10
va(i) = 5
end do
end subroutine a_test''')
loop = psyir.walk(Loop)[0]
otrans.apply(loop)
with pytest.raises(TransformationError) as err:
psytrans.validate(loop)
assert ("A PSyData node cannot be inserted between an OpenMP/ACC "
"directive and the loop(s)" in str(err.value))


def test_psy_data_trans_validate_no_elemental(fortran_reader):
'''Check that the transformation refuses to act on an elemental routine.'''
data_trans = PSyDataTrans()
psyir = fortran_reader.psyir_from_source('''\
elemental real function a_test(var)
real, intent(in) :: var
a_test = var*var
end function a_test''')
assign = psyir.walk(Assignment)[0]
with pytest.raises(TransformationError) as err:
data_trans.validate(assign)
assert ("Cannot add PSyData calls inside ELEMENTAL routine 'a_test' "
"because it would change its semantics" in str(err.value))


def test_class_definitions(fortran_writer):
'''Tests if the class-prefix can be set and behaves as expected.
'''
Expand Down Expand Up @@ -202,3 +238,22 @@ def test_trans_with_shape_function(monkeypatch, fortran_reader,
out = fortran_writer(psyir)
assert 'PreDeclareVariable("dummy", dummy)' in out
assert 'ProvideVariable("dummy", dummy)' in out


def test_psy_data_trans_remove_pure(fortran_reader):
'''
Test that applying the transformation to a pure routine causes that
attribute to be removed.

'''
psyir = fortran_reader.psyir_from_source('''\
pure subroutine so_clean(var)
integer, intent(inout) :: var
var = var*var
end subroutine so_clean
''')
routine = psyir.walk(Routine)[0]
assert routine.symbol.is_pure
psytrans = PSyDataTrans()
psytrans.apply(routine.children)
assert not routine.symbol.is_pure
Loading