Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(closes #2684) Fix the failure case when we have a WHERE construct with no array index notation. #2687

Merged
merged 34 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
628d732
Initial implementation to handle issue #2684
LonelyCat124 Aug 7, 2024
4719328
Further changes
LonelyCat124 Aug 7, 2024
72e13f2
Extra import still present after last commit
LonelyCat124 Aug 7, 2024
c9be14e
Extra import still present after last commit
LonelyCat124 Aug 7, 2024
79e9266
Switch to use the Reference2ArrayRangeTrans to simplify this
LonelyCat124 Aug 8, 2024
4752ef9
Error in the xfailing tests
LonelyCat124 Aug 8, 2024
b40ed65
Fixed issue with intrinsics (such as SUM) being broken and finished t…
LonelyCat124 Aug 8, 2024
7138f8b
Updates towards review
LonelyCat124 Aug 28, 2024
77558f2
Merge branch 'master' into 2684_fix
LonelyCat124 Aug 28, 2024
aef4b6e
flaking error
LonelyCat124 Aug 28, 2024
2fb1ba3
Fixed issues with imported symbols
LonelyCat124 Sep 2, 2024
83b3ab3
Merge branch 'master' into 2684_fix
LonelyCat124 Sep 10, 2024
94e82c7
fix import outside top level statements
LonelyCat124 Sep 11, 2024
8ac43e9
Merge branch '2684_fix' of github.com:stfc/PSyclone into 2684_fix
LonelyCat124 Sep 11, 2024
dcf8911
Failing coverage was unreachable, so removed
LonelyCat124 Sep 11, 2024
9ac2e32
Added a handler for non-reduction intrinsics
LonelyCat124 Sep 11, 2024
4163c4b
Added a test for non reduction intrinsic
LonelyCat124 Sep 12, 2024
3bd47f0
Handle the cases where there is a non elemental, non inquiry intrinsi…
LonelyCat124 Sep 18, 2024
d8443b6
Removed the check for inquiry intrinsics as they don't care about the…
LonelyCat124 Sep 18, 2024
200f28c
Test for missing coverage
LonelyCat124 Sep 19, 2024
198a866
Merge branch 'master' into 2684_fix
LonelyCat124 Sep 19, 2024
4ab7966
Improve implicitCall handling to also handle elemental functions
LonelyCat124 Sep 20, 2024
0d0db28
Merge branch '2684_fix' of github.com:stfc/PSyclone into 2684_fix
LonelyCat124 Sep 20, 2024
cdf2a78
Merge branch 'master' into 2684_fix
LonelyCat124 Sep 20, 2024
7772e88
Changes from the review
LonelyCat124 Sep 25, 2024
c169789
Merge branch 'master' into 2684_fix
LonelyCat124 Sep 26, 2024
e62f349
Fixed errors in nemo scripts
LonelyCat124 Sep 26, 2024
9681870
Extra comma removal
LonelyCat124 Sep 26, 2024
8e9d743
Readded continue to add kernels trans script that was removed in error
LonelyCat124 Sep 26, 2024
b8104b0
where handler change
LonelyCat124 Sep 27, 2024
4f21a41
Added AoS where test
LonelyCat124 Sep 27, 2024
07f3788
Add continue into the acc loops trans script to mimic previous behaviour
LonelyCat124 Sep 27, 2024
e714fcf
#2684 Update some comments
sergisiso Sep 30, 2024
9340fd6
#2684 Update changelog
sergisiso Sep 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions examples/nemo/scripts/acc_kernels_trans.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
from psyclone.psyGen import TransInfo
from psyclone.psyir.nodes import (
IfBlock, ArrayReference, Assignment, BinaryOperation, Loop, Routine,
Literal, Call, ACCLoopDirective)
Literal, ACCLoopDirective)
from psyclone.psyir.transformations import (ACCKernelsTrans, ACCUpdateTrans,
TransformationError, ProfileTrans)
from psyclone.transformations import ACCEnterDataTrans
Expand Down Expand Up @@ -395,14 +395,9 @@ def trans(psyir):
# In the lib_fortran file we annotate each routine that does not
# have a Loop or unsupported Calls with the OpenACC Routine Directive
if psyir.name == "lib_fortran.f90":
if not subroutine.walk(Loop):
calls = subroutine.walk(Call)
if all(call.is_available_on_device() for call in calls):
# SIGN_ARRAY_1D has a CodeBlock because of a WHERE without
# array notation. (TODO #717)
ACC_ROUTINE_TRANS.apply(subroutine,
options={"force": True})
continue
if subroutine.name.lower().startswith("sign_"):
ACC_ROUTINE_TRANS.apply(subroutine)
continue

# Attempt to add OpenACC directives unless we are ignoring this routine
if subroutine.name.lower() not in ACC_IGNORE:
Expand Down
16 changes: 5 additions & 11 deletions examples/nemo/scripts/acc_loops_trans.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
from utils import (
insert_explicit_loop_parallelism, normalise_loops, add_profiling,
enhance_tree_information, NOT_PERFORMANT, NOT_WORKING)
from psyclone.psyir.nodes import Loop, Routine
from psyclone.psyir.nodes import Routine
from psyclone.transformations import (
ACCParallelTrans, ACCLoopTrans, ACCRoutineTrans, TransformationError)
ACCParallelTrans, ACCLoopTrans, ACCRoutineTrans)

PROFILING_ENABLED = True

Expand Down Expand Up @@ -110,15 +110,9 @@ def trans(psyir):

# For performance in lib_fortran, mark serial routines as GPU-enabled
if psyir.name == "lib_fortran.f90":
if not subroutine.walk(Loop):
try:
# We need the 'force' option.
# SIGN_ARRAY_1D has a CodeBlock because of a WHERE without
# array notation. (TODO #717)
ACCRoutineTrans().apply(subroutine,
options={"force": True})
except TransformationError as err:
print(err)
if subroutine.name.lower().startswith("sign_"):
ACCRoutineTrans().apply(subroutine)
continue

insert_explicit_loop_parallelism(
subroutine,
Expand Down
11 changes: 2 additions & 9 deletions examples/nemo/scripts/omp_gpu_trans.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,8 @@ def trans(psyir):

# For performance in lib_fortran, mark serial routines as GPU-enabled
if psyir.name == "lib_fortran.f90":
if not subroutine.walk(Loop):
try:
# We need the 'force' option.
# SIGN_ARRAY_1D has a CodeBlock because of a WHERE without
# array notation. (TODO #717)
OMPDeclareTargetTrans().apply(subroutine,
options={"force": True})
except TransformationError as err:
print(err)
if subroutine.name.lower().startswith("sign_"):
OMPDeclareTargetTrans().apply(subroutine)

# For now this is a special case for stpctl.f90 because it forces
# loops to parallelise without many safety checks
Expand Down
154 changes: 55 additions & 99 deletions src/psyclone/psyir/frontend/fparser2.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@
Reference, Return, Routine, Schedule, StructureReference, UnaryOperation,
WhileLoop)
from psyclone.psyir.nodes.array_mixin import ArrayMixin
from psyclone.psyir.nodes.array_of_structures_mixin import (
ArrayOfStructuresMixin)
from psyclone.psyir.symbols import (
ArgumentInterface, ArrayType, AutomaticInterface, CHARACTER_TYPE,
CommonBlockInterface, ContainerSymbol, DataSymbol, DataTypeSymbol,
Expand Down Expand Up @@ -4179,75 +4177,6 @@ def _process_case_value(self, selector, node, parent):
parent.addchild(call)
call.children.extend(fake_parent.pop_all_children())

@staticmethod
def _array_notation_rank(node):
'''Check that the supplied candidate array reference uses supported
array notation syntax and return the rank of the sub-section
of the array that uses array notation. e.g. for a reference
"a(:, 2, :)" the rank of the sub-section is 2.

:param node: the reference to check.
:type node: :py:class:`psyclone.psyir.nodes.ArrayReference` or \
:py:class:`psyclone.psyir.nodes.ArrayMember` or \
:py:class:`psyclone.psyir.nodes.StructureReference`

:returns: rank of the sub-section of the array.
:rtype: int

:raises InternalError: if no ArrayMixin node with at least one \
Range in its indices is found.
:raises InternalError: if two or more part references in a \
structure reference contain ranges.
:raises NotImplementedError: if the supplied node is not of a \
supported type.
:raises NotImplementedError: if any ranges are encountered that are \
not for the full extent of the dimension.
'''
if isinstance(node, (ArrayReference, ArrayMember)):
array = node
elif isinstance(node, StructureReference):
array = None
arrays = node.walk((ArrayMember, ArrayOfStructuresMixin))
for part_ref in arrays:
if any(isinstance(idx, Range) for idx in part_ref.indices):
if array:
# Cannot have two or more part references that contain
# ranges - this is not valid Fortran.
raise InternalError(
f"Found a structure reference containing two or "
f"more part references that have ranges: "
f"'{node.debug_string()}'. This is not valid "
f"within a WHERE in Fortran.")
array = part_ref
if not array:
raise InternalError(
f"No array access found in node '{node.name}'")
else:
# This will result in a CodeBlock.
raise NotImplementedError(
f"Expected either an ArrayReference, ArrayMember or a "
f"StructureReference but got '{type(node).__name__}'")

# Only array refs using basic colon syntax are currently
# supported e.g. (a(:,:)). Each colon is represented in the
# PSyIR as a Range node with first argument being an lbound
# binary operator, the second argument being a ubound operator
# and the third argument being an integer Literal node with
# value 1 i.e. a(:,:) is represented as
# a(lbound(a,1):ubound(a,1):1,lbound(a,2):ubound(a,2):1) in
# the PSyIR.
num_colons = 0
for idx_node in array.indices:
if isinstance(idx_node, Range):
# Found array syntax notation. Check that it is the
# simple ":" format.
if not array.is_full_range(array.index_of(idx_node)):
raise NotImplementedError(
"Only array notation of the form my_array(:, :, ...) "
"is supported.")
num_colons += 1
return num_colons

def _array_syntax_to_indexed(self, parent, loop_vars):
'''
Utility function that modifies each ArrayReference object in the
Expand All @@ -4265,23 +4194,34 @@ def _array_syntax_to_indexed(self, parent, loop_vars):
:raises NotImplementedError: if array sections of differing ranks are
found.
'''
assigns = parent.walk(Assignment)
# Check that the LHS of any assignment uses array notation.
# Note that this will prevent any WHERE blocks that contain scalar
# assignments from being handled but is a necessary limitation until
# #717 is done and we interrogate the type of each symbol.
for assign in assigns:
_ = self._array_notation_rank(assign.lhs)

# TODO #717 if the supplied code accidentally omits array
# notation for an array reference on the RHS then we will
# identify it as a scalar and the code produced from the
# PSyIR (using e.g. the Fortran backend) will not
# compile. We need to implement robust identification of the
# types of all symbols in the PSyIR fragment.
# pylint: disable=import-outside-toplevel
from psyclone.psyir.transformations import (
Reference2ArrayRangeTrans, TransformationError)
# Convert References to arrays to use the array range notation unless
# they have an IntrinsicCall parent.
for ref in parent.walk(Reference):
if isinstance(ref.symbol.interface, (ImportInterface,
UnresolvedInterface)):
raise NotImplementedError(
"PSyclone doesn't yet support reference to imported "
"symbols inside WHERE clauses.")
call_ancestor = ref.ancestor(Call)
if (isinstance(ref.symbol, DataSymbol) and
not call_ancestor):
try:
Reference2ArrayRangeTrans().apply(ref)
except TransformationError:
pass
elif (isinstance(ref.symbol, DataSymbol) and call_ancestor
is not None and call_ancestor.is_elemental):
try:
Reference2ArrayRangeTrans().apply(ref)
except TransformationError:
pass
table = parent.scope.symbol_table
one = Literal("1", INTEGER_TYPE)
arrays = parent.walk(ArrayMixin)

first_rank = None
for array in arrays:
# Check that this is a supported array reference and that
Expand All @@ -4305,6 +4245,7 @@ def _array_syntax_to_indexed(self, parent, loop_vars):
base_ref = _copy_full_base_reference(array)
array_ref = array.ancestor(Reference, include_self=True)
if not isinstance(array_ref.datatype, ArrayType):
print(array_ref.debug_string())
raise NotImplementedError(
f"We can not get the resulting shape of the expression: "
f"{array_ref.debug_string()}")
Expand Down Expand Up @@ -4349,7 +4290,11 @@ def _array_syntax_to_indexed(self, parent, loop_vars):
expr = BinaryOperation.create(
add_op, lbound, Reference(symbol))
expr2 = BinaryOperation.create(sub_op, expr, one.copy())
array.children[idx] = expr2
# Indices of an AoSReference start at index 1
if isinstance(array, ArrayOfStructuresReference):
array.children[idx+1] = expr2
else:
array.children[idx] = expr2
range_idx += 1

def _where_construct_handler(self, node, parent):
Expand Down Expand Up @@ -4386,6 +4331,10 @@ def _where_construct_handler(self, node, parent):
not use array notation.

'''
# pylint: disable=import-outside-toplevel
from psyclone.psyir.transformations import (
Reference2ArrayRangeTrans, TransformationError)

sergisiso marked this conversation as resolved.
Show resolved Hide resolved
def _contains_intrinsic_reduction(pnodes):
'''
Utility to check for Fortran intrinsics that perform a reduction
Expand Down Expand Up @@ -4474,17 +4423,27 @@ def _contains_intrinsic_reduction(pnodes):
# parent for this logical expression we will repeat the processing.
fake_parent = Assignment(parent=parent)
self.process_nodes(fake_parent, logical_expr)
arrays = fake_parent.walk(ArrayMixin)
# We want to convert all the plain references that are arrays to use
# explicit array syntax.
# TODO 1799 If we have two arrays where one uses a non-maximal range
# that is defined we will not convert this correctly yet.
# Convert References to arrays to use the array range notation unless
# they have an IntrinsicCall parent.
references = fake_parent.walk(Reference)
for ref in references:
if isinstance(ref.symbol.interface, ImportInterface):
raise NotImplementedError(
"PSyclone doesn't yet support reference to imported "
"symbols inside WHERE clauses.")
intrinsic_ancestor = ref.ancestor(IntrinsicCall)
if (isinstance(ref.symbol, DataSymbol) and
not intrinsic_ancestor):
try:
Reference2ArrayRangeTrans().apply(ref)
except TransformationError:
pass
sergisiso marked this conversation as resolved.
Show resolved Hide resolved

if not arrays:
# If the PSyIR doesn't contain any Arrays then that must be
# because the code doesn't use explicit array syntax. At least one
# variable in the logical-array expression must be an array for
# this to be a valid WHERE().
# TODO #1799. Look-up the shape of the array in the SymbolTable.
raise NotImplementedError(
f"Only WHERE constructs using explicit array notation (e.g. "
f"my_array(:,:)) are supported but found '{logical_expr}'.")
arrays = fake_parent.walk(ArrayMixin)

for array in arrays:
if any(isinstance(idx, Range) for idx in array.indices):
Expand Down Expand Up @@ -4568,7 +4527,6 @@ def _contains_intrinsic_reduction(pnodes):
# handler returns
root_loop = loop
new_parent = sched

# Now we have the loop nest, add an IF block to the innermost
# schedule
ifblock = IfBlock(parent=new_parent, annotations=annotations)
Expand All @@ -4591,7 +4549,6 @@ def _contains_intrinsic_reduction(pnodes):
# Now construct the body of the IF using the body of the WHERE
sched = Schedule(parent=ifblock)
ifblock.addchild(sched)

if was_single_stmt:
# We only had a single-statement WHERE
self.process_nodes(sched, node.items[1:])
Expand Down Expand Up @@ -4663,7 +4620,6 @@ def _contains_intrinsic_reduction(pnodes):
# No elsewhere clauses were found so put the whole body into
# the single if block.
self.process_nodes(sched, node.content[1:-1])

# Convert all uses of array syntax to indexed accesses
self._array_syntax_to_indexed(ifblock, loop_vars)
# Return the top-level loop generated by this handler
Expand Down
Loading
Loading