Skip to content

Commit 2f6bd75

Browse files
authored
Merge pull request #3181 from stfc/3173_quickfix
(closes #3173) Add protections for CodeBlocks in the data_sharing_attribute_mixin
2 parents 7109d95 + 84a9492 commit 2f6bd75

File tree

4 files changed

+97
-32
lines changed

4 files changed

+97
-32
lines changed

changelog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
7) PR #3181 for #3173. Adds protections for CodeBlocks in the
2+
DataSharingAttributeMixin.
3+
14
6) PR #3174 and #3176 towards #2668. Add kwargs to more transformations.
25

36
5) PR #3186 for #3185. Small fix to SymbolicMaths.expand for Boolean

src/psyclone/psyir/nodes/data_sharing_attribute_mixin.py

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from psyclone.psyir.nodes.loop import Loop
4545
from psyclone.psyir.nodes.while_loop import WhileLoop
4646
from psyclone.psyir.nodes.omp_clauses import OMPReductionClause
47+
from psyclone.psyir.nodes.reference import Reference
4748
from psyclone.psyir.symbols import DataSymbol, Symbol
4849

4950

@@ -134,26 +135,42 @@ def infer_sharing_attributes(self) -> \
134135
continue
135136
symbol = accesses[0].node.scope.symbol_table.lookup(
136137
name, otherwise=None)
138+
if symbol is None:
139+
# The signature does not match any symbol! This is probably a
140+
# structure, we will consider them shared
141+
continue
142+
143+
# A parallel loop variable is always private
144+
if (isinstance(self.dir_body[0], Loop) and
145+
self.dir_body[0].variable is symbol):
146+
private.add(symbol)
147+
continue
137148

138149
# If it is manually marked as a local symbol, add it to private or
139150
# firstprivate set
140151
if (isinstance(symbol, DataSymbol) and
141152
isinstance(self.dir_body[0], Loop) and
142153
symbol in self.dir_body[0].explicitly_private_symbols):
143154
visited = set()
144-
if (
145-
# The loop variable is always private
146-
self.dir_body[0].variable is not symbol and
147-
# For anything else, if it uses a value coming from before
148-
# the loop scope, make it firstprivate
149-
any(access.node.enters_scope(self, visited_nodes=visited)
150-
for access in accesses)):
151-
fprivate.add(symbol)
155+
for access in accesses:
156+
if not isinstance(access.node, Reference):
157+
# Nodes that are not References do not have
158+
# 'enters_scope', so the analysis below can't be
159+
# done and we defensively use 'firstprivate'
160+
# TODO #3124: Remove this special-case
161+
fprivate.add(symbol)
162+
break
163+
if access.node.enters_scope(self, visited):
164+
# If it uses a value coming from before the loop
165+
# scope, make it 'firstprivate'
166+
fprivate.add(symbol)
167+
break
152168
else:
169+
# Everything else can be just 'private'
153170
private.add(symbol)
154171
continue
155172

156-
# All arrays not explicitly marked as threadprivate are shared
173+
# All arrays not explicitly marked as explicitly_private are shared
157174
if any(accs.has_indices() for accs in accesses):
158175
continue
159176

@@ -177,6 +194,7 @@ def infer_sharing_attributes(self) -> \
177194

178195
has_been_read = False
179196
last_read_position = 0
197+
visited_nodes = set() # To avoid enters_scope repetitions
180198
for access in accesses:
181199
if access.is_any_read():
182200
has_been_read = True
@@ -195,19 +213,13 @@ def infer_sharing_attributes(self) -> \
195213
limit=self,
196214
include_self=True)
197215
if not loop_ancestor:
198-
# If we find it at least once outside a loop we keep it
199-
# as shared
216+
# If we find it at least one WRITE outside a loop we
217+
# keep it as shared
200218
break
201219

202220
# Otherwise, the assignment to this variable is inside a
203221
# loop (and it will be repeated for each iteration), so
204-
# we declare it as private or need_sync
205-
name = signature.var_name
206-
# TODO #2094: var_name only captures the top-level
207-
# component in the derived type accessor. If the attributes
208-
# only apply to a sub-component, this won't be captured
209-
# appropriately.
210-
symbol = access.node.scope.symbol_table.lookup(name)
222+
# we declare it as [first]private or need_sync
211223

212224
# If it has been read before we have to check if ...
213225
if has_been_read:
@@ -220,23 +232,31 @@ def infer_sharing_attributes(self) -> \
220232
need_sync.add(symbol)
221233
break
222234

223-
# If the write is not guaranteed, we make it firstprivate
224-
# so that in the case that the write doesn't happen we keep
225-
# the original value
235+
# If the write is not guaranteed (because it is inside a
236+
# conditional), make it firstprivate to keep the original
237+
# value when the branch is never taken. Unless this never
238+
# had a value before the loop (as some compilers don't
239+
# like uninitialised firstprivates)
226240
conditional_write = access.node.ancestor(
227241
IfBlock,
228242
limit=loop_ancestor,
229243
include_self=True)
230244
if conditional_write:
231-
# If it's used before the loop, make it firstprivate
232-
visited = set()
233-
if any(access.node.enters_scope(
234-
self, visited_nodes=visited)
235-
for access in accesses):
236-
fprivate.add(symbol)
237-
break
238-
# Otherwise it is just private
239-
private.add(symbol)
245+
# Check if it gets a value from before the loop
246+
for access in accesses:
247+
if not isinstance(access.node, Reference):
248+
# Nodes that are not References do not have
249+
# 'enters_scope', so the analysis below can't
250+
# be done and we defensively use 'firstprivate'
251+
# TODO #3124: Remove this special-case
252+
fprivate.add(symbol)
253+
break
254+
if access.node.enters_scope(self, visited_nodes):
255+
fprivate.add(symbol)
256+
break
257+
# Otherwise it is just 'private'
258+
if symbol not in fprivate:
259+
private.add(symbol)
240260
break
241261

242262
return private, fprivate, need_sync

src/psyclone/psyir/transformations/parallel_loop_trans.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ def apply(self, node, options=None, verbose: bool = False,
374374
'''
375375
if not options:
376376
self.validate_options(
377-
verbose=verbose, collapse=collapse,
377+
verbose=verbose, collapse=collapse, force=force,
378378
ignore_dependencies_for=ignore_dependencies_for,
379379
privatise_arrays=privatise_arrays,
380380
sequential=sequential, nowait=nowait,
@@ -401,7 +401,7 @@ def apply(self, node, options=None, verbose: bool = False,
401401
reduction_ops = options.get("reduction_ops", [])
402402

403403
self.validate(node, options=options, verbose=verbose,
404-
collapse=collapse,
404+
collapse=collapse, force=force,
405405
ignore_dependencies_for=ignore_dependencies_for,
406406
privatise_arrays=privatise_arrays,
407407
sequential=sequential, nowait=nowait,

src/psyclone/tests/psyir/nodes/omp_directives_test.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,48 @@ def test_infer_sharing_attributes_with_explicitly_private_symbols(
676676
assert "scalar2" in [x.name for x in fpvars]
677677

678678

679+
def test_infer_sharing_attributes_with_codeblocks(
680+
fortran_reader, fortran_writer):
681+
''' Tests the infer_sharing_attributes() method when some of the loops have
682+
Codeblocks inside it. We check that the infer_sharing attribute analysis
683+
succeed by assuming worst case.
684+
'''
685+
psyir = fortran_reader.psyir_from_source('''
686+
subroutine my_subroutine()
687+
use other, only: mystruct
688+
integer :: i, j, scalar1 = 1, scalar2 = 2
689+
real, dimension(10) :: array, array2
690+
write(*,*) scalar1, scalar2
691+
do j = 1, 10
692+
do i = 1, size(array, 1)
693+
write(*,*) scalar2, mystruct(i)%field2
694+
if (.true.) then
695+
scalar1 = 1
696+
write(*,*) scalar1, mystruct(i)%field1
697+
end if
698+
scalar2 = scalar1 + 1
699+
write(*,*) scalar1, scalar2
700+
enddo
701+
enddo
702+
write(*,*) scalar1, scalar2
703+
end subroutine''')
704+
omplooptrans = OMPLoopTrans()
705+
omplooptrans.omp_directive = "paralleldo"
706+
loop = psyir.walk(Loop)[0]
707+
# Make sure that the write statements inside the loop are CodeBlocks,
708+
# otherwise we need a new test example
709+
assert loop.has_descendant(nodes.CodeBlock)
710+
loop.explicitly_private_symbols.add(
711+
loop.scope.symbol_table.lookup("scalar2"))
712+
omplooptrans.apply(loop, node_type_check=False, force=True)
713+
714+
# Here we mostly check that the infer_sharing attributes doesn't fall
715+
# over with CodeBlocks. This will often still be defensively firstprivate
716+
# as we condiser all codeblock accesses as READWRITE.
717+
output = fortran_writer(psyir)
718+
assert "firstprivate(scalar1,scalar2)" in output
719+
720+
679721
def test_infer_sharing_attributes(fortran_reader):
680722
''' Tests for the infer_sharing_attributes() method of OpenMP directives
681723
with generic code inside the directive body.

0 commit comments

Comments
 (0)