Skip to content

Commit

Permalink
Merge pull request #2880 from stfc/2723_improve_handling_of_ranges_in…
Browse files Browse the repository at this point in the history
…_dependency_analysis

#2723 Add some support for dependency analysis involving array ranges.
  • Loading branch information
sergisiso authored Feb 4, 2025
2 parents 06e0257 + 4aeae63 commit e68fa69
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 2 deletions.
3 changes: 3 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
17) PR #2829 for #2826. Adds a --config command-line argument to the
PSyAD script (to replicate that for psyclone).

18) PR #2280 for #2723. Add support for depencency analysis involving
array ranges.

release 3.0.0 6th of December 2024

1) PR #2477 for #2463. Add support for Fortran Namelist statements.
Expand Down
49 changes: 48 additions & 1 deletion src/psyclone/psyir/tools/dependency_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
from psyclone.errors import InternalError, LazyString
from psyclone.psyir.backend.sympy_writer import SymPyWriter
from psyclone.psyir.backend.visitor import VisitorError
from psyclone.psyir.nodes import Loop
from psyclone.psyir.nodes import Loop, Node, Range


# pylint: disable=too-many-lines
Expand Down Expand Up @@ -281,6 +281,50 @@ def _partition(comp_ind1, comp_ind2, loop_variables):

return partition_infos

# -------------------------------------------------------------------------
@staticmethod
def _ranges_overlap(range1: Node,
range2: Node) -> bool:
'''This function tests if two ranges overlap. It also accepts a simple
index as 'range' (e.g. just `i`), which will be converted into `i:i:1`
before comparing. At this stage, this function simple checks if one of
the ranges starts after the other (e.g. 1:3, and 5:7). It will handle
unspecified ranges (":"), and will report an overlap.
Additional tests e.g. using the step value are not yet implemented
(e.g. 1:10:2 and 2:10:2 will not overlap, but this will not be
detected atm).
:param range1: The first range or expression.
:param range2: The second range or expression.
:returns: whether the ranges (or an index expression with a range)
overlap or not
'''
if not isinstance(range1, Range):
# Not a range, must be some index `i`. Create a range `i:i:1`
range1 = Range.create(range1.copy(), range1.copy())
if not isinstance(range2, Range):
# Not a range, must be some index `i`. Create a range `i:i:1`
range2 = Range.create(range2.copy(), range2.copy())

sm = SymbolicMaths.get()

# Check if the first range is smaller than the second one, e.g.:
# 1:3:1 and 4:6:1
if sm.greater_than(range2.start, range1.stop) == sm.Fuzzy.TRUE:
# The first range is before the second range, so no overlap
return False
# Check if the second range is smaller than the first one, e.g.:
# 4:6:1 and 1:3:1
if sm.greater_than(range1.start, range2.stop) == sm.Fuzzy.TRUE:
# The second range is before the first range, so no overlap
return False

# We could do additional tests here, e.g. including step to determine
# that 1:10:2 does not overlap with 2:10:2
return True

# -------------------------------------------------------------------------
@staticmethod
def _independent_0_var(index_exp1, index_exp2):
Expand All @@ -296,6 +340,9 @@ def _independent_0_var(index_exp1, index_exp2):
:type index_exp2: :py:class:`psyclone.psyir.nodes.Node`
'''
if isinstance(index_exp1, Range) or isinstance(index_exp2, Range):
return not DependencyTools._ranges_overlap(index_exp1, index_exp2)

sym_maths = SymbolicMaths.get()

# If the indices can be shown to be never equal, the accesses
Expand Down
66 changes: 65 additions & 1 deletion src/psyclone/tests/psyir/tools/dependency_tools_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from psyclone.configuration import Config
from psyclone.core import AccessType, Signature, VariablesAccessInfo
from psyclone.errors import InternalError
from psyclone.psyir.nodes import Loop
from psyclone.psyir.nodes import Assignment, Loop
from psyclone.psyir.tools import DependencyTools, DTCode
from psyclone.tests.utilities import get_invoke

Expand Down Expand Up @@ -1107,3 +1107,67 @@ def test_fuse_dimension_change(fortran_reader):
"in different index locations: s%comp1(jj)%comp2(ji) and "
"s%comp1(ji)%comp2(jj)."
in str(msg))


# ----------------------------------------------------------------------------
@pytest.mark.parametrize("range1, range2, overlap",
[("1:3", "4:6", False),
("3:9", "-1:-3", False),
("1:3", "4", False),
("5", "-1:-3", False),
("i:i+3", "i+5:i+7", False),
("i:i+3", "i+2", True),
("i:i+3", "i+5", False),
("i:i+3", "i-1", False),
(":", "1", True),
(":", "i", True),
("::", "1", True),
("::", "i", True),
("1", ":", True),
("i", ":", True),
])
def test_ranges_overlap(range1, range2, overlap, fortran_reader):
'''Test the detection of overlapping ranges.
'''
source = f'''program test
integer i, ji, inbj
integer, parameter :: jpi=5, jpj=10
real, dimension(jpi,jpi) :: ldisoce
ldisoce({range1},{range2}) = 1.0
end program test'''

psyir = fortran_reader.psyir_from_source(source)
dep_tools = DependencyTools()
assign = psyir.walk(Assignment)[0]
r1 = assign.lhs.children[0]
r2 = assign.lhs.children[1]
assert dep_tools._ranges_overlap(r1, r2) == overlap
# Also make sure that _independent_0_var handles this correctly:
assert dep_tools._independent_0_var(r1, r2) is not overlap


# ----------------------------------------------------------------------------
def test_nemo_example_ranges(fortran_reader):
'''Tests an actual NEMO example
'''
source = '''program test
integer ji, inbj
integer, parameter :: jpi=5, jpj=10
real, dimension(jpi,jpi) :: ldisoce
do jj = 1, inbj, 1
if (COUNT(ldisoce(:,jj)) == 0) then
ldisoce(1,jj) = .true.
end if
enddo
end program test'''

psyir = fortran_reader.psyir_from_source(source)
loops = psyir.children[0].children[0]
dep_tools = DependencyTools()

# This loop can be parallelised because all instances of ldisoce use
# the index jj in position 2 (the overlap between ":" and "1"
# is tested in test_ranges_overlap above, here we check that this
# overlap is indeed ignored because of the jj index).
assert dep_tools.can_loop_be_parallelised(loops)

0 comments on commit e68fa69

Please sign in to comment.