Skip to content

Commit e8dd4c7

Browse files
committed
#2878 Add some support for dependency analysis involving array ranges.
1 parent 06e0257 commit e8dd4c7

File tree

2 files changed

+113
-2
lines changed

2 files changed

+113
-2
lines changed

src/psyclone/psyir/tools/dependency_tools.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
from psyclone.errors import InternalError, LazyString
4949
from psyclone.psyir.backend.sympy_writer import SymPyWriter
5050
from psyclone.psyir.backend.visitor import VisitorError
51-
from psyclone.psyir.nodes import Loop
51+
from psyclone.psyir.nodes import Loop, Node, Range
5252

5353

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

282282
return partition_infos
283283

284+
# -------------------------------------------------------------------------
285+
@staticmethod
286+
def _ranges_overlap(range1: Node,
287+
range2: Node) -> bool:
288+
'''This function tests if two ranges overlap. It also accepts a simple
289+
index as 'range' (e.g. just `i`), which will be converted into `i:i:1`
290+
before comparing. At this stage, this function simple checks if one of
291+
the ranges starts after the other (e.g. 1:3, and 5:7). It will handle
292+
unspecified ranges (":"), and will report an overlap.
293+
Additional tests e.g. using the step value are not yet implemented
294+
(e.g. 1:10:2 and 2:10:2 will not overlap, but this will not be
295+
detected atm).
296+
297+
:param range1: The first range or expression.
298+
:param range2: The second range or expression.
299+
300+
:returns: whether the ranges (or an index expression with a range)
301+
overlap or not
302+
303+
'''
304+
if not isinstance(range1, Range):
305+
# Not a range, must be some index `i`. Create a range `i:i:1`
306+
range1 = Range.create(range1.copy(), range1.copy())
307+
if not isinstance(range2, Range):
308+
# Not a range, must be some index `i`. Create a range `i:i:1`
309+
range2 = Range.create(range2.copy(), range2.copy())
310+
311+
sm = SymbolicMaths.get()
312+
313+
# Check if the first range is smaller than the second one, e.g.:
314+
# 1:3:1 and 4:6:1
315+
if sm.greater_than(range2.start, range1.stop) == sm.Fuzzy.TRUE:
316+
# The first range is before the second range, so no overlap
317+
return False
318+
# Check if the second range is smaller than the first one, e.g.:
319+
# 4:6:1 and 1:3:1
320+
if sm.greater_than(range1.start, range2.stop) == sm.Fuzzy.TRUE:
321+
# The second range is before the first range, so no overlap
322+
return False
323+
324+
# We could do additional tests here, e.g. including step to determine
325+
# that 1:10:2 does not overlap with 2:10:2
326+
return True
327+
284328
# -------------------------------------------------------------------------
285329
@staticmethod
286330
def _independent_0_var(index_exp1, index_exp2):
@@ -296,6 +340,9 @@ def _independent_0_var(index_exp1, index_exp2):
296340
:type index_exp2: :py:class:`psyclone.psyir.nodes.Node`
297341
298342
'''
343+
if isinstance(index_exp1, Range) or isinstance(index_exp2, Range):
344+
return not DependencyTools._ranges_overlap(index_exp1, index_exp2)
345+
299346
sym_maths = SymbolicMaths.get()
300347

301348
# If the indices can be shown to be never equal, the accesses

src/psyclone/tests/psyir/tools/dependency_tools_test.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
from psyclone.configuration import Config
4242
from psyclone.core import AccessType, Signature, VariablesAccessInfo
4343
from psyclone.errors import InternalError
44-
from psyclone.psyir.nodes import Loop
44+
from psyclone.psyir.nodes import Assignment, Loop
4545
from psyclone.psyir.tools import DependencyTools, DTCode
4646
from psyclone.tests.utilities import get_invoke
4747

@@ -1107,3 +1107,67 @@ def test_fuse_dimension_change(fortran_reader):
11071107
"in different index locations: s%comp1(jj)%comp2(ji) and "
11081108
"s%comp1(ji)%comp2(jj)."
11091109
in str(msg))
1110+
1111+
1112+
# ----------------------------------------------------------------------------
1113+
@pytest.mark.parametrize("range1, range2, overlap",
1114+
[("1:3", "4:6", False),
1115+
("3:9", "-1:-3", False),
1116+
("1:3", "4", False),
1117+
("5", "-1:-3", False),
1118+
("i:i+3", "i+5:i+7", False),
1119+
("i:i+3", "i+2", True),
1120+
("i:i+3", "i+5", False),
1121+
("i:i+3", "i-1", False),
1122+
(":", "1", True),
1123+
(":", "i", True),
1124+
("::", "1", True),
1125+
("::", "i", True),
1126+
("1", ":", True),
1127+
("i", ":", True),
1128+
])
1129+
def test_ranges_overlap(range1, range2, overlap, fortran_reader):
1130+
'''Test the detection of overlapping ranges.
1131+
'''
1132+
source = f'''program test
1133+
integer i, ji, inbj
1134+
integer, parameter :: jpi=5, jpj=10
1135+
real, dimension(jpi,jpi) :: ldisoce
1136+
1137+
ldisoce({range1},{range2}) = 1.0
1138+
end program test'''
1139+
1140+
psyir = fortran_reader.psyir_from_source(source)
1141+
dep_tools = DependencyTools()
1142+
assign = psyir.walk(Assignment)[0]
1143+
r1 = assign.lhs.children[0]
1144+
r2 = assign.lhs.children[1]
1145+
assert dep_tools._ranges_overlap(r1, r2) == overlap
1146+
# Also make sure that _independent_0_var handles this correctly:
1147+
assert dep_tools._independent_0_var(r1, r2) is not overlap
1148+
1149+
1150+
# ----------------------------------------------------------------------------
1151+
def test_nemo_example_ranges(fortran_reader):
1152+
'''Tests an actual NEMO example
1153+
'''
1154+
source = '''program test
1155+
integer ji, inbj
1156+
integer, parameter :: jpi=5, jpj=10
1157+
real, dimension(jpi,jpi) :: ldisoce
1158+
do jj = 1, inbj, 1
1159+
if (COUNT(ldisoce(:,jj)) == 0) then
1160+
ldisoce(1,jj) = .true.
1161+
end if
1162+
enddo
1163+
end program test'''
1164+
1165+
psyir = fortran_reader.psyir_from_source(source)
1166+
loops = psyir.children[0].children[0]
1167+
dep_tools = DependencyTools()
1168+
1169+
# This loop can be parallelised because all instances of ldisoce use
1170+
# the index jj in position 2 (the overlap between ":" and "1"
1171+
# is tested in test_ranges_overlap above, here we check that this
1172+
# overlap is indeed ignored because of the jj index).
1173+
assert dep_tools.can_loop_be_parallelised(loops)

0 commit comments

Comments
 (0)