From 256650eaaf4672b572accd4346be63b290f7da14 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Wed, 8 Apr 2026 09:22:44 +0100 Subject: [PATCH 1/7] Use all intrinsic reduction expansion transformations in NEMO --- examples/nemo/scripts/utils.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/nemo/scripts/utils.py b/examples/nemo/scripts/utils.py index 6ebe7ffbdd..7639ac8380 100755 --- a/examples/nemo/scripts/utils.py +++ b/examples/nemo/scripts/utils.py @@ -45,9 +45,10 @@ from psyclone.psyir.symbols import DataSymbol from psyclone.psyir.transformations import ( ArrayAssignment2LoopsTrans, HoistLoopBoundExprTrans, HoistLocalArraysTrans, - HoistTrans, InlineTrans, Maxval2LoopTrans, ProfileTrans, - OMPMinimiseSyncTrans, Reference2ArrayRangeTrans, - ScalarisationTrans, IncreaseRankLoopArraysTrans, MaximalRegionTrans) + HoistTrans, InlineTrans, Maxval2LoopTrans, Sum2LoopTrans, Minval2LoopTrans, + Product2LoopTrans, ProfileTrans, OMPMinimiseSyncTrans, + Reference2ArrayRangeTrans, ScalarisationTrans, IncreaseRankLoopArraysTrans, + MaximalRegionTrans) from psyclone.transformations import TransformationError # USE statements to chase to gather additional symbol information. @@ -219,11 +220,17 @@ def normalise_loops( if loopify_array_intrinsics: for intr in schedule.walk(IntrinsicCall): - if intr.intrinsic.name == "MAXVAL": - try: + try: + if intr.intrinsic.name == "MAXVAL": Maxval2LoopTrans().apply(intr, verbose=True) - except TransformationError as err: - print(err.value) + elif intr.intrinsic.name == "SUM": + Sum2LoopTrans().apply(intr, verbose=True) + elif intr.intrinsic.name == "MINVAL": + Minval2LoopTrans().apply(intr, verbose=True) + elif intr.intrinsic.name == "PRODUCT": + Product2LoopTrans().apply(intr, verbose=True) + except TransformationError as err: + print(err.value) if convert_range_loops: # Convert all array implicit loops to explicit loops From 8ef35848357a4b06ae433d9b6452efc71899a969 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Wed, 8 Apr 2026 11:28:11 +0100 Subject: [PATCH 2/7] Fix issue with intrinsic datatype --- src/psyclone/psyir/nodes/intrinsic_call.py | 21 +++++++++---------- .../tests/psyir/nodes/intrinsic_call_test.py | 11 ++++++++-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index cea33cea06..b0f1a24e8c 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -241,8 +241,7 @@ def _type_of_named_arg_with_optional_kind_and_dim( def _type_with_specified_precision_and_optional_dim( node: IntrinsicCall, argument_name: str, - intrinsic: ScalarType.Intrinsic = ScalarType.Intrinsic.BOOLEAN - ) -> DataType: +) -> DataType: """Helper function for the common IntrinsicCall case where the return type is a Scalar with the precision of a named argument, unless an optional argument named 'dim' exists, in which case an array @@ -250,17 +249,19 @@ def _type_with_specified_precision_and_optional_dim( :param node: The IntrinsicCall whose return type to compute. :param argument_name: The name of the argument whose precision to be used. - :param intrinsic: The type of the intrinsic of the resulting datatype. - Default is ScalarType.Intrinsic.BOOLEAN :returns: the computed datatype for the IntrinsicCall. """ - dtype = ScalarType( - intrinsic, node.argument_by_name(argument_name).datatype.precision - ) - # If dim is not present, or the rank of the - # array argument is 1 then this returns a scalar. arg = node.argument_by_name(argument_name) + arg_dt = arg.datatype + if ( + not isinstance(arg_dt, ArrayType) or + not isinstance(arg_dt.elemental_type, ScalarType) or + not isinstance(arg_dt.elemental_type.intrinsic, ScalarType.Intrinsic) + ): + return UnresolvedType() + dtype = arg_dt.elemental_type + # If dim is not present, return the same datatype if "dim" not in node.argument_names: return dtype @@ -3885,7 +3886,6 @@ class Intrinsic(IAttr, Enum): lambda node: _type_with_specified_precision_and_optional_dim( node, "array", - node.argument_by_name("array").datatype.intrinsic ) ), reference_accesses=lambda node: ( @@ -4542,7 +4542,6 @@ class Intrinsic(IAttr, Enum): lambda node: _type_with_specified_precision_and_optional_dim( node, "array", - node.argument_by_name("array").datatype.intrinsic ) ), reference_accesses=lambda node: ( diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index 8e5a4f232f..b7929eeec8 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -1068,23 +1068,25 @@ def test_type_with_specified_precision_and_optional_dim(fortran_reader): """Test the _type_with_specified_precision_and_optional_dim helper function.""" code = """subroutine test + use other integer, dimension(100, 100) :: x integer :: y y = PRODUCT(x) y = PRODUCT(x, dim=2) + y = PRODUCT(z, dim=2) end subroutine test""" psyir = fortran_reader.psyir_from_source(code) intrinsics = psyir.walk(IntrinsicCall) dtype = _type_with_specified_precision_and_optional_dim( - intrinsics[0], "array", ScalarType.Intrinsic.INTEGER, + intrinsics[0], "array" ) assert isinstance(dtype, ScalarType) assert dtype.intrinsic == ScalarType.Intrinsic.INTEGER assert dtype.precision == ScalarType.Precision.UNDEFINED dtype = _type_with_specified_precision_and_optional_dim( - intrinsics[1], "array", ScalarType.Intrinsic.INTEGER, + intrinsics[1], "array" ) assert isinstance(dtype, ArrayType) assert len(dtype.shape) == 1 @@ -1092,6 +1094,11 @@ def test_type_with_specified_precision_and_optional_dim(fortran_reader): assert dtype.intrinsic == ScalarType.Intrinsic.INTEGER assert dtype.precision == ScalarType.Precision.UNDEFINED + dtype = _type_with_specified_precision_and_optional_dim( + intrinsics[2], "array" + ) + assert isinstance(dtype, UnresolvedType) + def test_type_of_intrinsic_with_precision_of_named_arg(fortran_reader): """Test the _type_of_intrinsic_with_precision_of_named_arg helper From a0b3625604be65c79c7feb449f56957994166955 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Thu, 9 Apr 2026 10:40:52 +0100 Subject: [PATCH 3/7] Fix issue with dependency_tool when it can not find a symbol --- src/psyclone/psyir/tools/dependency_tools.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/psyclone/psyir/tools/dependency_tools.py b/src/psyclone/psyir/tools/dependency_tools.py index 7cf69bcbfb..bf8942d3b7 100644 --- a/src/psyclone/psyir/tools/dependency_tools.py +++ b/src/psyclone/psyir/tools/dependency_tools.py @@ -898,7 +898,9 @@ def can_loop_be_parallelised(self, loop, except AttributeError: # If its a node without a symbol, look it up var_name = signature.var_name - symbol = symbol_table.lookup(var_name) + symbol = symbol_table.lookup(var_name, otherwise=None) + if symbol is None: + return False # TODO #1270 - the is_array_access function might be moved is_array = symbol.is_array_access(access_info=var_info) From b9741f52e74db94b843a1f7e865f747ba5c35940 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Fri, 10 Apr 2026 16:13:27 +0100 Subject: [PATCH 4/7] Change TODOs of #1270 to #3098 --- src/psyclone/psyir/symbols/symbol.py | 2 +- src/psyclone/psyir/tools/dependency_tools.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/psyclone/psyir/symbols/symbol.py b/src/psyclone/psyir/symbols/symbol.py index be8f863f8b..1580b446a7 100644 --- a/src/psyclone/psyir/symbols/symbol.py +++ b/src/psyclone/psyir/symbols/symbol.py @@ -499,7 +499,7 @@ def is_array_access(self, index_variable=None, access_info=None): access information is given. ''' - # TODO #1270: this function might either be better off elsewhere, + # TODO #3098: this function might either be better off elsewhere, # or even do not implement one function that uses both access # information and symbol table - if required, the user can # query both in two simple statements anyway. diff --git a/src/psyclone/psyir/tools/dependency_tools.py b/src/psyclone/psyir/tools/dependency_tools.py index bf8942d3b7..5bc8a3d9b4 100644 --- a/src/psyclone/psyir/tools/dependency_tools.py +++ b/src/psyclone/psyir/tools/dependency_tools.py @@ -902,7 +902,7 @@ def can_loop_be_parallelised(self, loop, if symbol is None: return False - # TODO #1270 - the is_array_access function might be moved + # TODO #3098 - the is_array_access function might be moved is_array = symbol.is_array_access(access_info=var_info) if is_array: # Handle arrays @@ -985,7 +985,7 @@ def can_loops_be_fused(self, loop1, loop2): continue symbol = symbol_table.lookup(signature.var_name) - # TODO #1270 - the is_array_access function might be moved + # TODO #3098 - the is_array_access function might be moved is_array = symbol.is_array_access(access_info=var_info1) if not is_array: result = self._fuse_validate_written_scalar(var_info1, From 49e63e3837aac966c3cefffb155a8eb13857bdb9 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Fri, 10 Apr 2026 16:33:15 +0100 Subject: [PATCH 5/7] #3396 Improve intrinsiccall method name and docstring --- src/psyclone/psyir/nodes/intrinsic_call.py | 20 +++++++++---------- .../tests/psyir/nodes/intrinsic_call_test.py | 12 +++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index b0f1a24e8c..d29064bca4 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -239,16 +239,16 @@ def _type_of_named_arg_with_optional_kind_and_dim( return _type_of_arg_with_rank_minus_one(arg, dtype) -def _type_with_specified_precision_and_optional_dim( +def _type_of_named_arg_accounting_for_dim_arg( node: IntrinsicCall, argument_name: str, ) -> DataType: """Helper function for the common IntrinsicCall case where the - return type is a Scalar with the precision of a named argument, - unless an optional argument named 'dim' exists, in which case an array - with rank one less than the input node is given instead. + return type is the same than a given named argument. If intrinsiccall + has no 'dim' argument, it returns its elemental type, but if 'dim' + exists, it will be the given named argument rank minus one. - :param node: The IntrinsicCall whose return type to compute. - :param argument_name: The name of the argument whose precision to be used. + :param node: the IntrinsicCall whose return type to compute. + :param argument_name: the name of the argument whose type to use. :returns: the computed datatype for the IntrinsicCall. """ @@ -876,7 +876,7 @@ class Intrinsic(IAttr, Enum): optional_args={"dim": DataNode}, return_type=( lambda node: - _type_with_specified_precision_and_optional_dim( + _type_of_named_arg_accounting_for_dim_arg( node, "mask" ) ), @@ -944,7 +944,7 @@ class Intrinsic(IAttr, Enum): optional_args={"dim": DataNode}, return_type=( lambda node: - _type_with_specified_precision_and_optional_dim( + _type_of_named_arg_accounting_for_dim_arg( node, "mask" ) ), @@ -3884,7 +3884,7 @@ class Intrinsic(IAttr, Enum): optional_args={"mask": DataNode}, return_type=( lambda node: - _type_with_specified_precision_and_optional_dim( + _type_of_named_arg_accounting_for_dim_arg( node, "array", ) ), @@ -4540,7 +4540,7 @@ class Intrinsic(IAttr, Enum): optional_args={"mask": DataNode}, return_type=( lambda node: - _type_with_specified_precision_and_optional_dim( + _type_of_named_arg_accounting_for_dim_arg( node, "array", ) ), diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index b7929eeec8..9b760c2ed6 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -61,7 +61,7 @@ _type_of_arg_with_rank_minus_one, _type_of_named_argument, _type_of_named_arg_with_optional_kind_and_dim, - _type_with_specified_precision_and_optional_dim, + _type_of_named_arg_accounting_for_dim_arg, _type_of_scalar_with_optional_kind, _type_of_intrinsic_with_argname_kind_and_optional_dim, _type_of_intrinsic_with_precision_of_named_arg, @@ -1064,8 +1064,8 @@ def test_type_of_named_arg_with_optional_kind_and_dim( assert dtype.precision.value == "8" -def test_type_with_specified_precision_and_optional_dim(fortran_reader): - """Test the _type_with_specified_precision_and_optional_dim +def test_type_of_named_arg_accounting_for_dim_arg(fortran_reader): + """Test the _type_of_named_arg_accounting_for_dim_arg helper function.""" code = """subroutine test use other @@ -1078,14 +1078,14 @@ def test_type_with_specified_precision_and_optional_dim(fortran_reader): psyir = fortran_reader.psyir_from_source(code) intrinsics = psyir.walk(IntrinsicCall) - dtype = _type_with_specified_precision_and_optional_dim( + dtype = _type_of_named_arg_accounting_for_dim_arg( intrinsics[0], "array" ) assert isinstance(dtype, ScalarType) assert dtype.intrinsic == ScalarType.Intrinsic.INTEGER assert dtype.precision == ScalarType.Precision.UNDEFINED - dtype = _type_with_specified_precision_and_optional_dim( + dtype = _type_of_named_arg_accounting_for_dim_arg( intrinsics[1], "array" ) assert isinstance(dtype, ArrayType) @@ -1094,7 +1094,7 @@ def test_type_with_specified_precision_and_optional_dim(fortran_reader): assert dtype.intrinsic == ScalarType.Intrinsic.INTEGER assert dtype.precision == ScalarType.Precision.UNDEFINED - dtype = _type_with_specified_precision_and_optional_dim( + dtype = _type_of_named_arg_accounting_for_dim_arg( intrinsics[2], "array" ) assert isinstance(dtype, UnresolvedType) From 74ce535908dbbda49edab8ba498b090d8c756c76 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 21 Apr 2026 13:27:40 +0100 Subject: [PATCH 6/7] Doc fixes --- src/psyclone/psyir/nodes/intrinsic_call.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index f2963d1d63..9b42a183b2 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -243,9 +243,9 @@ def _type_of_named_arg_accounting_for_dim_arg( node: IntrinsicCall, argument_name: str, ) -> DataType: """Helper function for the common IntrinsicCall case where the - return type is the same than a given named argument. If intrinsiccall + return type is the same as the given named argument. If intrinsiccall has no 'dim' argument, it returns its elemental type, but if 'dim' - exists, it will be the given named argument rank minus one. + exists, it will be the given named argument's rank minus one. :param node: the IntrinsicCall whose return type to compute. :param argument_name: the name of the argument whose type to use. From 9a3d5b8703eed63f6b0a7603a1175bd5f189bc23 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 21 Apr 2026 13:30:49 +0100 Subject: [PATCH 7/7] #1658 update changelog --- changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog b/changelog index 3f43a5e0c0..793ae1557a 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ + 6) PR #3369 towards #1658. Switches NEMO scripts to use all of the + supported reduction intrinsic expansion transformations. + 5) PR #3377 for #2612. Add support for Fortran character length. 4) PR #3388 for #3334. Remove the functionality to write (and rename)