From 97d4099fa8955674db63ad26b08087a38ca62a95 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 7 May 2026 10:42:39 +0100 Subject: [PATCH 01/12] Disable application to only iom_put --- examples/nemo/scripts/utils.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/nemo/scripts/utils.py b/examples/nemo/scripts/utils.py index 40005514a5..4c76da01eb 100755 --- a/examples/nemo/scripts/utils.py +++ b/examples/nemo/scripts/utils.py @@ -550,13 +550,13 @@ def iom_put_argument_to_temporary(calls: list[Call]): ''' for call in calls: - if call.symbol.name == "iom_put": - for arg in call.arguments: - dtype = arg.datatype - if (isinstance(dtype, ArrayType) and - (isinstance(arg, Operation) or - isinstance(arg, IntrinsicCall))): - try: - DataNodeToTempTrans().apply(arg, verbose=True) - except TransformationError: - pass + #if call.symbol.name == "iom_put": + for arg in call.arguments: + dtype = arg.datatype + if (isinstance(dtype, ArrayType) and + (isinstance(arg, Operation) or + isinstance(arg, IntrinsicCall))): + try: + DataNodeToTempTrans().apply(arg, verbose=True) + except TransformationError: + pass From 77b08232564add21c6512ff5e622af99e13e6ffb Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 7 May 2026 11:54:28 +0100 Subject: [PATCH 02/12] Don't do it for characters --- examples/nemo/scripts/utils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/nemo/scripts/utils.py b/examples/nemo/scripts/utils.py index 4c76da01eb..896a825401 100755 --- a/examples/nemo/scripts/utils.py +++ b/examples/nemo/scripts/utils.py @@ -43,7 +43,7 @@ Assignment, Loop, Directive, Node, Reference, CodeBlock, Call, Routine, Schedule, IntrinsicCall, StructureReference, IfBlock, Operation) -from psyclone.psyir.symbols import DataSymbol, ArrayType +from psyclone.psyir.symbols import DataSymbol, ArrayType, ScalarType from psyclone.psyir.transformations import ( ArrayAssignment2LoopsTrans, HoistLoopBoundExprTrans, HoistLocalArraysTrans, HoistTrans, InlineTrans, Maxval2LoopTrans, Sum2LoopTrans, Minval2LoopTrans, @@ -550,13 +550,17 @@ def iom_put_argument_to_temporary(calls: list[Call]): ''' for call in calls: - #if call.symbol.name == "iom_put": + # if call.symbol.name == "iom_put": for arg in call.arguments: dtype = arg.datatype if (isinstance(dtype, ArrayType) and (isinstance(arg, Operation) or isinstance(arg, IntrinsicCall))): try: + if (isinstance(dtype.elemental_type, ScalarType) + and dtype.elemental_type.intrinsic == + ScalarType.Intrinsic.CHARACTER): + continue DataNodeToTempTrans().apply(arg, verbose=True) except TransformationError: pass From 9ada128fae4a5070345bc8a6ad4aa0e29dcd19b3 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 7 May 2026 14:18:06 +0100 Subject: [PATCH 03/12] Potential fix to datanodetotemptrans, tests NYI] --- .../psyir/transformations/datanode_to_temp_trans.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/psyclone/psyir/transformations/datanode_to_temp_trans.py b/src/psyclone/psyir/transformations/datanode_to_temp_trans.py index 78505d31ef..99085fae61 100644 --- a/src/psyclone/psyir/transformations/datanode_to_temp_trans.py +++ b/src/psyclone/psyir/transformations/datanode_to_temp_trans.py @@ -48,6 +48,7 @@ Loop, Range, Reference, + Routine, Statement, Schedule, UnaryOperation, @@ -343,14 +344,19 @@ def apply(self, node: DataNode, storage_name: str = "", allocatable_datatype.shape]) # Create a symbol of the relevant type. + containing_routine = node.ancestor(Routine) + if containing_routine: + sym_tab = containing_routine.symbol_table + else: + sym_tab = node.scope.symbol_table if not storage_name: - symbol = node.scope.symbol_table.new_symbol( + symbol = sym_tab.new_symbol( root_name="tmp", symbol_type=DataSymbol, datatype=datatype ) else: - symbol = node.scope.symbol_table.new_symbol( + symbol = sym_tab.new_symbol( root_name=storage_name, symbol_type=DataSymbol, datatype=datatype From 228d4087eda6274914230e4a1eb6cd62e3a0d5ca Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com> Date: Tue, 12 May 2026 11:21:54 +0100 Subject: [PATCH 04/12] Improvements to Ref2ArrayRange trans --- .../transformations/reference2arrayrange_trans.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/psyclone/psyir/transformations/reference2arrayrange_trans.py b/src/psyclone/psyir/transformations/reference2arrayrange_trans.py index a345d58ea4..6411c0a442 100644 --- a/src/psyclone/psyir/transformations/reference2arrayrange_trans.py +++ b/src/psyclone/psyir/transformations/reference2arrayrange_trans.py @@ -47,6 +47,9 @@ ArrayReference, Call, Reference, Member, StructureReference, ArrayOfStructuresMember, ArrayOfStructuresReference, ArrayMember, StructureMember, Assignment, Range) +from psyclone.psyir.nodes.intrinsic_call import ( + IntrinsicCall, REDUCTION_INTRINSICS +) from psyclone.psyir.nodes.structure_accessor_mixin import ( StructureAccessorMixin) from psyclone.psyir.nodes.array_mixin import ArrayMixin @@ -232,15 +235,18 @@ def apply(self, node, options=None, **kwargs): ''' self.validate(node, **kwargs) - # The following cases do not need expansions if node.parent and isinstance(node.parent, Call): if node is node.parent.routine: return - if not node.parent.is_elemental: + if not ((isinstance(node.parent, IntrinsicCall) and + node.parent.intrinsic in REDUCTION_INTRINSICS) + or node.parent.is_elemental): return if node and node.parent and isinstance(node.parent, Range): return +# if node.symbol.is_unresolved: +# return # Recurse down the node converting each plain Reference and Member # to ArrayReferences or ArrayMembers when they are associated to an From 3de798f57942522ae80ce220a65fd89e6abb288e Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com> Date: Thu, 14 May 2026 11:52:36 +0100 Subject: [PATCH 05/12] changes to nemo infrastructure to order of operations, and fix to array reduction base trans. Testing NYI --- examples/nemo/scripts/utils.py | 22 +++++++++++++++---- .../transformations/datanode_to_temp_trans.py | 5 ++++- .../intrinsics/array_reduction_base_trans.py | 8 +++++++ .../reference2arrayrange_trans.py | 8 ++----- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/examples/nemo/scripts/utils.py b/examples/nemo/scripts/utils.py index 896a825401..2e1f04f518 100755 --- a/examples/nemo/scripts/utils.py +++ b/examples/nemo/scripts/utils.py @@ -205,10 +205,6 @@ def normalise_loops( :param hoist_argument_expressions: whether to hoist array expressions out of the containing Call. ''' - # TODO #3412: This is currently limited to iom_put, we want to expand it - # throughout the code - if hoist_argument_expressions: - iom_put_argument_to_temporary(schedule.walk(Call)) if hoist_local_arrays and schedule.name not in CONTAINS_STMT_FUNCTIONS: # Apply the HoistLocalArraysTrans when possible, it cannot be applied @@ -225,6 +221,9 @@ def normalise_loops( Reference2ArrayRangeTrans().apply(reference) except TransformationError: pass + except Exception as err: + print(reference, reference.parent.parent.debug_string()) + raise err if loopify_array_intrinsics: for intr in schedule.walk(IntrinsicCall): @@ -280,6 +279,21 @@ def normalise_loops( except TransformationError: pass + # TODO #3412: This is currently limited to iom_put, we want to expand it + # throughout the code + if hoist_argument_expressions: + iom_put_argument_to_temporary(schedule.walk(Call)) + normalise_loops( + schedule, + hoist_local_arrays=hoist_local_arrays, + convert_array_notation=convert_array_notation, + loopify_array_intrinsics=loopify_array_intrinsics, + convert_range_loops=convert_range_loops, + scalarise_loops=scalarise_loops, + increase_array_ranks=False, + hoist_expressions=hoist_expressions, + hoist_argument_expressions=False, # Make sure we never repeat this. + ) # TODO #1928: In order to perform better on the GPU, nested loops with two # sibling inner loops need to be fused or apply loop fission to the # top level. This would allow the collapse clause to be applied. diff --git a/src/psyclone/psyir/transformations/datanode_to_temp_trans.py b/src/psyclone/psyir/transformations/datanode_to_temp_trans.py index 99085fae61..c9aeba5728 100644 --- a/src/psyclone/psyir/transformations/datanode_to_temp_trans.py +++ b/src/psyclone/psyir/transformations/datanode_to_temp_trans.py @@ -231,7 +231,10 @@ def validate(self, node: DataNode, **kwargs): f"Statement node which is not supported." ) - if isinstance(dtype, (UnresolvedType, UnsupportedFortranType)): + if (isinstance(dtype, (UnresolvedType, UnsupportedFortranType)) + or (isinstance(dtype, ArrayType) and + isinstance(dtype.elemental_type, + (UnresolvedType, UnsupportedFortranType)))): failing_symbols = [] symbols = node.get_all_accessed_symbols() for sym in symbols: diff --git a/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py b/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py index 05be7ea9d4..cda88a1e20 100644 --- a/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py +++ b/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py @@ -119,6 +119,14 @@ def validate(self, node, options=None, **kwargs): f"disambiguate the arguments names in '{node.debug_string()}'" ) from err + # We can enter here with References to an array expression, which + # must be blocked. + if not node.walk(ArrayReference): + raise TransformationError( + f"Error in {self.name}. No ArrayReferences found in " + f"'{node.debug_string()}'." + ) + array_ref = node.argument_by_name("array") dim_ref = node.argument_by_name("dim") diff --git a/src/psyclone/psyir/transformations/reference2arrayrange_trans.py b/src/psyclone/psyir/transformations/reference2arrayrange_trans.py index 6411c0a442..ee4d5f1c73 100644 --- a/src/psyclone/psyir/transformations/reference2arrayrange_trans.py +++ b/src/psyclone/psyir/transformations/reference2arrayrange_trans.py @@ -235,18 +235,14 @@ def apply(self, node, options=None, **kwargs): ''' self.validate(node, **kwargs) - # The following cases do not need expansions + # The following cases do not need expansions if node.parent and isinstance(node.parent, Call): if node is node.parent.routine: return - if not ((isinstance(node.parent, IntrinsicCall) and - node.parent.intrinsic in REDUCTION_INTRINSICS) - or node.parent.is_elemental): + if not node.parent.is_elemental: return if node and node.parent and isinstance(node.parent, Range): return -# if node.symbol.is_unresolved: -# return # Recurse down the node converting each plain Reference and Member # to ArrayReferences or ArrayMembers when they are associated to an From 8061ceb929c4ef81d217130a50104d5015864d96 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 14 May 2026 15:05:10 +0100 Subject: [PATCH 06/12] fixed tests and failing cases while maintaining expected results --- .../intrinsics/array_reduction_base_trans.py | 17 ++++++++--------- .../reference2arrayrange_trans.py | 5 +---- .../intrinsics/sum2loop_trans_test.py | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py b/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py index cda88a1e20..a9a7ab4310 100644 --- a/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py +++ b/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py @@ -119,14 +119,6 @@ def validate(self, node, options=None, **kwargs): f"disambiguate the arguments names in '{node.debug_string()}'" ) from err - # We can enter here with References to an array expression, which - # must be blocked. - if not node.walk(ArrayReference): - raise TransformationError( - f"Error in {self.name}. No ArrayReferences found in " - f"'{node.debug_string()}'." - ) - array_ref = node.argument_by_name("array") dim_ref = node.argument_by_name("dim") @@ -134,7 +126,6 @@ def validate(self, node, options=None, **kwargs): raise TransformationError( f"The dimension argument to {self._INTRINSIC_NAME} is not " f"yet supported.") - if isinstance(node.datatype, (UnresolvedType, UnsupportedType)): raise TransformationError( f"Error in {self.name} transformation. Cannot create " @@ -186,6 +177,9 @@ def apply(self, node, options=None, verbose: bool = False, **kwargs): :param options: options for the transformation. :type options: Optional[Dict[str, Any]] + :raises TransformationError: if node only contains Reference nodes, + and they can't be convered to ArrayReferences by the calls to + Reference2ArrayRangeTrans. ''' # TODO 2668: options are now deprecated: if options: @@ -229,6 +223,11 @@ def apply(self, node, options=None, verbose: bool = False, **kwargs): # resulting in the following code being created: # a(:,:) = a(:,:)+b(:,:) array_refs = new_array_expr.walk(ArrayReference) + if len(array_refs) == 0: + raise TransformationError( + f"Can't apply {self.name} to {node.debug_string()} due " + f"to no ArrayReference nodes present." + ) assignment = Assignment.create(array_refs[0].copy(), new_array_expr.detach()) diff --git a/src/psyclone/psyir/transformations/reference2arrayrange_trans.py b/src/psyclone/psyir/transformations/reference2arrayrange_trans.py index ee4d5f1c73..acb9b95ce8 100644 --- a/src/psyclone/psyir/transformations/reference2arrayrange_trans.py +++ b/src/psyclone/psyir/transformations/reference2arrayrange_trans.py @@ -47,9 +47,6 @@ ArrayReference, Call, Reference, Member, StructureReference, ArrayOfStructuresMember, ArrayOfStructuresReference, ArrayMember, StructureMember, Assignment, Range) -from psyclone.psyir.nodes.intrinsic_call import ( - IntrinsicCall, REDUCTION_INTRINSICS -) from psyclone.psyir.nodes.structure_accessor_mixin import ( StructureAccessorMixin) from psyclone.psyir.nodes.array_mixin import ArrayMixin @@ -235,7 +232,7 @@ def apply(self, node, options=None, **kwargs): ''' self.validate(node, **kwargs) - # The following cases do not need expansions + # The following cases do not need expansions if node.parent and isinstance(node.parent, Call): if node is node.parent.routine: return diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py index b3cf7fa303..ee5527b4e4 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py @@ -141,3 +141,21 @@ def test_apply(fortran_reader, fortran_writer, tmpdir): result = fortran_writer(psyir) assert expected in result assert Compile(tmpdir).string_compiles(result) + + +def test_nemo5_transerror_case(fortran_reader): + '''Test that the transformation fails correctly if we have nested sums + as the reference can't be converted to an arrayreference.''' + code = """subroutine sum_test() + integer :: n, m + real , dimension(:, :) :: array + + result = sum(sum(array, dim=2)) * array(n,m) + end subroutine""" + psyir = fortran_reader.psyir_from_source(code) + intrinsic_node = psyir.children[0].children[0].rhs.children[0] + trans = Sum2LoopTrans() + with pytest.raises(TransformationError) as err: + trans.apply(intrinsic_node) + assert ("Can't apply Sum2LoopTrans to SUM(SUM(array, 2)) due " + "to no ArrayReference nodes present." in str(err.value)) From d70c54b35498d49e872e68fbcfc66b8b01d9c880 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 14 May 2026 15:18:13 +0100 Subject: [PATCH 07/12] linting --- examples/nemo/scripts/utils.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/nemo/scripts/utils.py b/examples/nemo/scripts/utils.py index 2e1f04f518..c5058882bd 100755 --- a/examples/nemo/scripts/utils.py +++ b/examples/nemo/scripts/utils.py @@ -284,15 +284,16 @@ def normalise_loops( if hoist_argument_expressions: iom_put_argument_to_temporary(schedule.walk(Call)) normalise_loops( - schedule, - hoist_local_arrays=hoist_local_arrays, - convert_array_notation=convert_array_notation, - loopify_array_intrinsics=loopify_array_intrinsics, - convert_range_loops=convert_range_loops, - scalarise_loops=scalarise_loops, - increase_array_ranks=False, - hoist_expressions=hoist_expressions, - hoist_argument_expressions=False, # Make sure we never repeat this. + schedule, + hoist_local_arrays=hoist_local_arrays, + convert_array_notation=convert_array_notation, + loopify_array_intrinsics=loopify_array_intrinsics, + convert_range_loops=convert_range_loops, + scalarise_loops=scalarise_loops, + increase_array_ranks=False, + hoist_expressions=hoist_expressions, + # Make sure we never repeat this step. + hoist_argument_expressions=False, ) # TODO #1928: In order to perform better on the GPU, nested loops with two # sibling inner loops need to be fused or apply loop fission to the From 483f51de548a852de6998ef2b276c73bcc72af98 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 15 May 2026 14:53:20 +0100 Subject: [PATCH 08/12] Fix coverage --- .../datanode_to_temp_trans_test.py | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py b/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py index 647ecba4c3..b335322178 100644 --- a/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py @@ -40,7 +40,7 @@ from psyclone.configuration import Config from psyclone.psyir.frontend.fortran import FortranReader from psyclone.psyir.nodes import ( - Assignment, Reference + Assignment, Loop, Reference, Routine ) from psyclone.psyir.symbols import ( DataSymbol, INTEGER_TYPE @@ -299,6 +299,9 @@ def test_datanodetotemptrans_apply(fortran_reader, fortran_writer, tmp_path): psyir = fortran_reader.psyir_from_source(code) assign = psyir.walk(Assignment)[0] dtrans.apply(assign.rhs.operands[1]) + # Check the tmp symbol is at the routine scope. + routine = psyir.walk(Routine)[0] + assert routine.symbol_table.lookup("tmp") is not None out = fortran_writer(psyir) assert """ integer, allocatable, dimension(:,:) :: tmp @@ -309,6 +312,27 @@ def test_datanodetotemptrans_apply(fortran_reader, fortran_writer, tmp_path): d = c + tmp""" in out assert Compile(tmp_path).string_compiles(out) + # Check the symbol is still created if the assignment is in some other + # non-routine scope. + code = """subroutine test() + integer :: a, b, c + integer :: i + + do i = 1, 100 + a = b + c + end do + end subroutine test""" + psyir = fortran_reader.psyir_from_source(code) + loop = psyir.walk(Loop)[0].detach() + assign = loop.walk(Assignment)[0] + dtrans.apply(assign.rhs) + assert assign.scope.symbol_table.lookup("tmp") is not None + out = fortran_writer(loop) + assert """do i = 1, 100, 1 + tmp = b + c + a = tmp +enddo""" in out + code = """subroutine test() real :: a integer :: b From 7fa1410c97b7cb2039b0b8de014b1a8069393571 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 19 May 2026 13:08:08 +0100 Subject: [PATCH 09/12] First changes towards the review --- examples/nemo/scripts/utils.py | 16 ++++++---------- .../datanode_to_temp_trans_test.py | 2 +- .../intrinsics/sum2loop_trans_test.py | 19 +++++++++++++++++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/examples/nemo/scripts/utils.py b/examples/nemo/scripts/utils.py index c5058882bd..07626d086b 100755 --- a/examples/nemo/scripts/utils.py +++ b/examples/nemo/scripts/utils.py @@ -221,9 +221,6 @@ def normalise_loops( Reference2ArrayRangeTrans().apply(reference) except TransformationError: pass - except Exception as err: - print(reference, reference.parent.parent.debug_string()) - raise err if loopify_array_intrinsics: for intr in schedule.walk(IntrinsicCall): @@ -279,10 +276,8 @@ def normalise_loops( except TransformationError: pass - # TODO #3412: This is currently limited to iom_put, we want to expand it - # throughout the code if hoist_argument_expressions: - iom_put_argument_to_temporary(schedule.walk(Call)) + hoist_arguments_to_temporaries(schedule.walk(Call)) normalise_loops( schedule, hoist_local_arrays=hoist_local_arrays, @@ -555,9 +550,9 @@ def _satisfies_minimum_region_rules(self, region: list[Node]) -> bool: MaximalProfilingOutsideDirectivesTrans().apply(children) -def iom_put_argument_to_temporary(calls: list[Call]): - '''Extracts the second argument of all iom_put calls and puts them - in a temporary if they are an Operation with an array datatype. +def hoist_arguments_to_temporaries(calls: list[Call]): + '''Extracts the arguments of all calls and puts them + in a temporary result if they are an Operation with an array datatype. :param calls: The list of calls in a subroutine whose arguments may be moved into temporary storage to allow additional potential @@ -565,9 +560,10 @@ def iom_put_argument_to_temporary(calls: list[Call]): ''' for call in calls: - # if call.symbol.name == "iom_put": for arg in call.arguments: dtype = arg.datatype + # Only extract expressions that can potentially be loopfied, + # i.e. operations over arrays. if (isinstance(dtype, ArrayType) and (isinstance(arg, Operation) or isinstance(arg, IntrinsicCall))): diff --git a/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py b/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py index b335322178..80c315c522 100644 --- a/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py @@ -326,7 +326,7 @@ def test_datanodetotemptrans_apply(fortran_reader, fortran_writer, tmp_path): loop = psyir.walk(Loop)[0].detach() assign = loop.walk(Assignment)[0] dtrans.apply(assign.rhs) - assert assign.scope.symbol_table.lookup("tmp") is not None + assert "tmp" in assign.scope.symbol_table out = fortran_writer(loop) assert """do i = 1, 100, 1 tmp = b + c diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py index ee5527b4e4..7082118f4d 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py @@ -143,9 +143,10 @@ def test_apply(fortran_reader, fortran_writer, tmpdir): assert Compile(tmpdir).string_compiles(result) -def test_nemo5_transerror_case(fortran_reader): +def test_nested_sums_error_case(fortran_reader, fortran_writer): '''Test that the transformation fails correctly if we have nested sums - as the reference can't be converted to an arrayreference.''' + as the reference can't be converted to an arrayreference as the reference + is inside a non-elemental function.''' code = """subroutine sum_test() integer :: n, m real , dimension(:, :) :: array @@ -159,3 +160,17 @@ def test_nemo5_transerror_case(fortran_reader): trans.apply(intrinsic_node) assert ("Can't apply Sum2LoopTrans to SUM(SUM(array, 2)) due " "to no ArrayReference nodes present." in str(err.value)) + + #code = """subroutine sum_test() + #integer :: n, m + #real, dimension(:, :) :: array + #result = sum(sum(array + array(:,:), dim=2)) + #end subroutine""" + #psyir = fortran_reader.psyir_from_source(code) + #intrinsic_node = psyir.children[0].children[0].rhs + #trans = Sum2LoopTrans() + #with pytest.raises(TransformationError) as err: + # trans.apply(intrinsic_node) + #print(fortran_writer(psyir)) + #print(err.value) + #assert False From 1a894b5424a8b33e044ccefc53182945b4df0a16 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 19 May 2026 13:12:32 +0100 Subject: [PATCH 10/12] linting issue --- .../intrinsics/sum2loop_trans_test.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py index 7082118f4d..973433b9e3 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py @@ -161,16 +161,16 @@ def test_nested_sums_error_case(fortran_reader, fortran_writer): assert ("Can't apply Sum2LoopTrans to SUM(SUM(array, 2)) due " "to no ArrayReference nodes present." in str(err.value)) - #code = """subroutine sum_test() - #integer :: n, m - #real, dimension(:, :) :: array - #result = sum(sum(array + array(:,:), dim=2)) - #end subroutine""" - #psyir = fortran_reader.psyir_from_source(code) - #intrinsic_node = psyir.children[0].children[0].rhs - #trans = Sum2LoopTrans() - #with pytest.raises(TransformationError) as err: - # trans.apply(intrinsic_node) - #print(fortran_writer(psyir)) - #print(err.value) - #assert False + # code = """subroutine sum_test() + # integer :: n, m + # real, dimension(:, :) :: array + # result = sum(sum(array + array(:,:), dim=2)) + # end subroutine""" + # psyir = fortran_reader.psyir_from_source(code) + # intrinsic_node = psyir.children[0].children[0].rhs + # trans = Sum2LoopTrans() + # with pytest.raises(TransformationError) as err: + # trans.apply(intrinsic_node) + # print(fortran_writer(psyir)) + # print(err.value) + # assert False From 5cfcdeb799b28e66f1826cd49cf6f65e352cd376 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 19 May 2026 14:28:51 +0100 Subject: [PATCH 11/12] Added other tests --- .../intrinsics/sum2loop_trans_test.py | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py index 973433b9e3..d7b809d035 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/sum2loop_trans_test.py @@ -143,13 +143,14 @@ def test_apply(fortran_reader, fortran_writer, tmpdir): assert Compile(tmpdir).string_compiles(result) -def test_nested_sums_error_case(fortran_reader, fortran_writer): +def test_nested_sums_error_cases(fortran_reader): '''Test that the transformation fails correctly if we have nested sums as the reference can't be converted to an arrayreference as the reference is inside a non-elemental function.''' code = """subroutine sum_test() integer :: n, m real , dimension(:, :) :: array + real :: result result = sum(sum(array, dim=2)) * array(n,m) end subroutine""" @@ -161,16 +162,38 @@ def test_nested_sums_error_case(fortran_reader, fortran_writer): assert ("Can't apply Sum2LoopTrans to SUM(SUM(array, 2)) due " "to no ArrayReference nodes present." in str(err.value)) - # code = """subroutine sum_test() - # integer :: n, m - # real, dimension(:, :) :: array - # result = sum(sum(array + array(:,:), dim=2)) - # end subroutine""" - # psyir = fortran_reader.psyir_from_source(code) - # intrinsic_node = psyir.children[0].children[0].rhs - # trans = Sum2LoopTrans() - # with pytest.raises(TransformationError) as err: - # trans.apply(intrinsic_node) - # print(fortran_writer(psyir)) - # print(err.value) - # assert False + code = """subroutine sum_test() + integer :: n, m + real, dimension(:, :) :: array + real :: result + + result = sum(sum(array + array(:,:), dim=2)) + end subroutine""" + psyir = fortran_reader.psyir_from_source(code) + intrinsic_node = psyir.children[0].children[0].rhs + trans = Sum2LoopTrans() + with pytest.raises(TransformationError) as err: + trans.apply(intrinsic_node) + assert ("Transformation Error: ArrayAssignment2LoopsTrans does not " + "support statements containing dependencies that would generate " + "loop-carried dependencies when naively converting them to a " + "loop, but found" in str(err.value)) + + code = """subroutine sum_test() + integer :: n, m + real, dimension(:, :) :: array + integer, dimension(1) :: dimensions + real :: result + result = sum(sum(array, dim=dimensions(2))) + end subroutine""" + psyir = fortran_reader.psyir_from_source(code) + intrinsic_node = psyir.children[0].children[0].rhs + trans = Sum2LoopTrans() + with pytest.raises(TransformationError) as err: + trans.apply(intrinsic_node) + assert ("Transformation Error: Error in ArrayAssignment2LoopsTrans " + "transformation. The LHS of the supplied Assignment node " + "should contain an array accessor with at least one of its " + "dimensions being a Range, but none were found in " + "'dimensions(2) = SUM(array, dimensions(2))" + in str(err.value)) From bb5b2aa7b8f859231ce95cf28585cc71384d7e8b Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Wed, 27 May 2026 15:46:54 +0100 Subject: [PATCH 12/12] #3412 Update changelog and minor fixes --- changelog | 3 +++ examples/nemo/scripts/utils.py | 2 ++ .../tests/psyir/transformations/datanode_to_temp_trans_test.py | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/changelog b/changelog index f0bc7c9311..7828991949 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ + 24) PR #3429 for #3412. Generalise hoist_arguments_to_temporaries in NEMO utils + to all arguments with array operations. + 23) PR #3436 for #3135. Fixes some cyclic dependencies by replacing the ScalarType import-time instantiations. diff --git a/examples/nemo/scripts/utils.py b/examples/nemo/scripts/utils.py index 090a9a5643..0dc1cf3dab 100755 --- a/examples/nemo/scripts/utils.py +++ b/examples/nemo/scripts/utils.py @@ -569,6 +569,8 @@ def hoist_arguments_to_temporaries(calls: list[Call]): (isinstance(arg, Operation) or isinstance(arg, IntrinsicCall))): try: + # TODO #3443: Does the fixes in that PR fix this, or + # maybe the DataNodeToTempTrans has to skip characters if (isinstance(dtype.elemental_type, ScalarType) and dtype.elemental_type.intrinsic == ScalarType.Intrinsic.CHARACTER): diff --git a/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py b/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py index 453486cbd8..ba5798e270 100644 --- a/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/datanode_to_temp_trans_test.py @@ -301,7 +301,7 @@ def test_datanodetotemptrans_apply(fortran_reader, fortran_writer, tmp_path): dtrans.apply(assign.rhs.operands[1]) # Check the tmp symbol is at the routine scope. routine = psyir.walk(Routine)[0] - assert routine.symbol_table.lookup("tmp") is not None + assert "tmp" in routine.symbol_table out = fortran_writer(psyir) assert """ integer, allocatable, dimension(:,:) :: tmp