diff --git a/loki/expression/tests/test_expression.py b/loki/expression/tests/test_expression.py index 3a0d85f80..7c2dd22be 100644 --- a/loki/expression/tests/test_expression.py +++ b/loki/expression/tests/test_expression.py @@ -473,11 +473,11 @@ def test_index_ranges(frontend): # OMNI will insert implicit lower=1 into shape declarations, # we simply have to live with it... :( assert str(vmap['v4']) == 'v4(dim)' or str(vmap['v4']) == 'v4(1:dim)' - assert str(vmap['v5']) == 'v5(1:dim)' + assert str(vmap['v5']) == 'v5(1:dim)' or str(vmap['v5']) == 'v5(dim)' vmap_body = {v.name: v for v in FindVariables().visit(routine.body)} assert str(vmap_body['v1']) == 'v1(::2)' - assert str(vmap_body['v2']) == 'v2(1:dim)' + assert str(vmap_body['v2']) == 'v2(dim)' or str(vmap_body['v2']) == 'v2(1:dim)' assert str(vmap_body['v3']) == 'v3(0:4:2)' assert str(vmap_body['v5']) == 'v5(:)' @@ -1853,13 +1853,8 @@ def to_str(_parsed): assert isinstance(parsed, sym.Array) assert all(isinstance(_parsed, sym.Scalar) for _parsed in parsed.dimensions) assert all(_parsed.scope == routine for _parsed in parsed.dimensions) - if frontend == OMNI: - assert all(isinstance(_parsed, sym.RangeIndex) for _parsed in parsed.shape) - assert all(isinstance(_parsed.upper, sym.Scalar) for _parsed in parsed.shape) - assert all(_parsed.upper.scope == routine for _parsed in parsed.shape) - else: - assert all(isinstance(_parsed, sym.Scalar) for _parsed in parsed.shape) - assert all(_parsed.scope == routine for _parsed in parsed.shape) + assert all(isinstance(_parsed, sym.Scalar) for _parsed in parsed.shape) + assert all(_parsed.scope == routine for _parsed in parsed.shape) assert to_str(parsed) == 'arr(i1,i2,i3)' parsed = parse_expr(convert_to_case('my_func(i1)', mode=case), scope=routine) diff --git a/loki/frontend/tests/test_frontends.py b/loki/frontend/tests/test_frontends.py index 4adeb7f63..f6d308cf8 100644 --- a/loki/frontend/tests/test_frontends.py +++ b/loki/frontend/tests/test_frontends.py @@ -212,12 +212,9 @@ def test_associates(tmp_path, frontend): module = Module.from_source(fcode, frontend=frontend, xmods=[tmp_path]) routine = module['associates'] variables = FindVariables().visit(routine.body) - if frontend == OMNI: - assert all(v.shape == ('1:3',) - for v in variables if v.name in ['vector', 'vector2']) - else: - assert all(v.shape == ('3',) - for v in variables if v.name in ['vector', 'vector2']) + assert all( + v.shape == ('3',) for v in variables if v.name in ['vector', 'vector2'] + ) for assoc in FindNodes(ir.Associate).visit(routine.body): for var in FindVariables().visit(assoc.body): diff --git a/loki/frontend/tests/test_omni.py b/loki/frontend/tests/test_omni.py new file mode 100644 index 000000000..cb0372467 --- /dev/null +++ b/loki/frontend/tests/test_omni.py @@ -0,0 +1,85 @@ +# (C) Copyright 2018- ECMWF. +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. + +""" +Specific test battery for the OMNI parser frontend. +""" + +import pytest + +from loki import Module, Subroutine +from loki.expression import FindVariables +from loki.frontend import OMNI, HAVE_OMNI +from loki.ir import nodes as ir, FindNodes + + +@pytest.mark.skipif(not HAVE_OMNI, reason='Test tequires OMNI frontend.') +def test_derived_type_definitions(tmp_path): + """ Test correct parsing of derived type declarations. """ + fcode = """ +module omni_derived_type_mod + type explicit + real(kind=8) :: scalar, vector(3), matrix(3, 3) + end type explicit + + type deferred + real(kind=8), allocatable :: scalar, vector(:), matrix(:, :) + end type deferred + + type ranged + real(kind=8) :: scalar, vector(1:3), matrix(0:3, 0:3) + end type ranged +end module omni_derived_type_mod +""" + # Parse the source and validate the IR + module = Module.from_source(fcode, frontend=OMNI, xmods=[tmp_path]) + + assert len(module.typedefs) == 3 + explicit_symbols = FindVariables(unique=False).visit(module['explicit'].body) + assert explicit_symbols == ('scalar', 'vector(3)', 'matrix(3, 3)') + + deferred_symbols = FindVariables(unique=False).visit(module['deferred'].body) + assert deferred_symbols == ('scalar', 'vector(:)', 'matrix(:, :)') + + ranged_symbols = FindVariables(unique=False).visit(module['ranged'].body) + assert ranged_symbols == ('scalar', 'vector(3)', 'matrix(0:3, 0:3)') + + +@pytest.mark.skipif(not HAVE_OMNI, reason='Test tequires OMNI frontend.') +def test_array_dimensions(tmp_path): + """ Test correct parsing of derived type declarations. """ + fcode = """ +subroutine omni_array_indexing(n, a, b) + integer, intent(in) :: n + real(kind=8), intent(inout) :: a(3), b(n) + real(kind=8) :: c(n, n) + real(kind=8) :: d(1:n, 0:n) + + a(:) = 11. + b(1:n) = 42. + c(2:n, 0:n) = 66. + d(:, 0:n) = 68. +end subroutine omni_array_indexing +""" + # Parse the source and validate the IR + routine = Subroutine.from_source(fcode, frontend=OMNI, xmods=[tmp_path]) + + # OMNI separate declarations per variable + decls = FindNodes(ir.VariableDeclaration).visit(routine.spec) + assert len(decls) == 5 + assert decls[0].symbols == ('n',) + assert decls[1].symbols == ('a(3)',) + assert decls[2].symbols == ('b(n)',) + assert decls[3].symbols == ('c(n, n)',) + assert decls[4].symbols == ('d(n, 0:n)',) + + assigns = FindNodes(ir.Assignment).visit(routine.body) + assert len(assigns) == 4 + assert assigns[0].lhs == 'a(:)' + assert assigns[1].lhs == 'b(1:n)' + assert assigns[2].lhs == 'c(2:n, 0:n)' + assert assigns[3].lhs == 'd(:, 0:n)' diff --git a/loki/frontend/util.py b/loki/frontend/util.py index 8e55e307a..5bf794326 100644 --- a/loki/frontend/util.py +++ b/loki/frontend/util.py @@ -11,6 +11,9 @@ from codetiming import Timer from more_itertools import split_after +from loki.expression import ( + symbols as sym, SubstituteExpressionsMapper, ExpressionRetriever +) from loki.ir import ( NestedTransformer, FindNodes, PatternFinder, Transformer, Assignment, Comment, CommentBlock, VariableDeclaration, @@ -267,6 +270,35 @@ def visit_tuple(self, o, **kwargs): return tuple(i for i in visited if i is not None and as_tuple(i)) +class RangeIndexTransformer(Transformer): + """ + :any:`Transformer` that replaces ``arr(1:n)`` notations with + ``arr(n)`` in :any:`VariableDeclaration`. + """ + + retriever = ExpressionRetriever(lambda e: isinstance(e, (sym.Array))) + + @staticmethod + def is_one_index(dim): + return isinstance(dim, sym.RangeIndex) and dim.lower == 1 and dim.step is None + + def visit_VariableDeclaration(self, o, **kwargs): # pylint: disable=unused-argument + """ + Gets all :any:`Array` symbols and adjusts dimension and shape. + """ + vmap = {} + for v in self.retriever.retrieve(o.symbols): + dimensions = tuple(d.upper if self.is_one_index(d) else d for d in v.dimensions) + _type = v.type + if _type.shape: + shape = tuple(d.upper if self.is_one_index(d) else d for d in _type.shape) + _type = _type.clone(shape=shape) + vmap[v] = v.clone(dimensions=dimensions, type=_type) + + mapper = SubstituteExpressionsMapper(vmap, invalidate_source=self.invalidate_source) + return o.clone(symbols=mapper(o.symbols, recurse_to_declaration_attributes=True)) + + @Timer(logger=perf, text=lambda s: f'[Loki::Frontend] Executed sanitize_ir in {s:.2f}s') def sanitize_ir(_ir, frontend, pp_registry=None, pp_info=None): """ @@ -303,6 +335,10 @@ def sanitize_ir(_ir, frontend, pp_registry=None, pp_info=None): _ir = InlineCommentTransformer(inplace=True, invalidate_source=False).visit(_ir) _ir = ClusterCommentTransformer(inplace=True, invalidate_source=False).visit(_ir) + if frontend == OMNI: + # Revert OMNI's array dimension expansion from `a(n)` => `arr(1:n)` + _ir = RangeIndexTransformer(invalidate_source=False).visit(_ir) + if frontend in (OMNI, OFP): _ir = inline_labels(_ir) diff --git a/loki/tests/test_derived_types.py b/loki/tests/test_derived_types.py index e4049a213..dcd3c8609 100644 --- a/loki/tests/test_derived_types.py +++ b/loki/tests/test_derived_types.py @@ -1173,12 +1173,8 @@ def test_derived_type_rescope_symbols_shadowed(tmp_path, shadowed_typedef_symbol assert istate in ('istate(nmaxstreams)', 'istate(1:nmaxstreams)') assert istate.scope is rng_type - if frontend == OMNI: - assert istate.dimensions[0] == '1:nmaxstreams' - assert istate.dimensions[0].stop.scope - else: - assert istate.dimensions[0] == 'nmaxstreams' - assert istate.dimensions[0].scope + assert istate.dimensions[0] == 'nmaxstreams' + assert istate.dimensions[0].scope # FIXME: Use of NMaxStreams from parent scope is in the wrong scope (LOKI-52) #assert istate.dimensions[0].scope is module diff --git a/loki/tests/test_modules.py b/loki/tests/test_modules.py index d48eeb504..0f844b940 100644 --- a/loki/tests/test_modules.py +++ b/loki/tests/test_modules.py @@ -90,7 +90,7 @@ def test_module_external_typedefs_subroutine(frontend, tmp_path): pt_ext = routine.variables[0] # OMNI resolves explicit shape parameters in the frontend parser - exptected_array_shape = '(1:2, 1:3)' if frontend == OMNI else '(x, y)' + exptected_array_shape = '(2, 3)' if frontend == OMNI else '(x, y)' # Check that the `array` variable in the `ext` type is found and # has correct type and shape info @@ -187,7 +187,7 @@ def test_module_external_typedefs_type(frontend, tmp_path): assert isinstance(module['other_routine'].symbol_attrs['pt'].dtype.typedef, TypeDef) # OMNI resolves explicit shape parameters in the frontend parser - exptected_array_shape = '(1:2, 1:3)' if frontend == OMNI else '(x, y)' + exptected_array_shape = '(2, 3)' if frontend == OMNI else '(x, y)' # Check that the `array` variable in the `ext` type is found and # has correct type and shape info @@ -234,7 +234,7 @@ def test_module_nested_types(frontend, tmp_path): end module type_mod """ # OMNI resolves explicit shape parameters in the frontend parser - exptected_array_shape = '(1:2, 1:3)' if frontend == OMNI else '(x, y)' + exptected_array_shape = '(2, 3)' if frontend == OMNI else '(x, y)' module = Module.from_source(fcode, frontend=frontend, xmods=[tmp_path]) parent = module.typedef_map['parent_type'] diff --git a/loki/tests/test_subroutine.py b/loki/tests/test_subroutine.py index a7cd2baaa..9a6d543b4 100644 --- a/loki/tests/test_subroutine.py +++ b/loki/tests/test_subroutine.py @@ -15,8 +15,7 @@ Array, Scalar, Variable, SymbolAttributes, StringLiteral, fgen, fexprgen, VariableDeclaration, Transformer, FindTypedSymbols, - ProcedureSymbol, StatementFunction, - normalize_range_indexing, DeferredTypeSymbol + ProcedureSymbol, StatementFunction, DeferredTypeSymbol ) from loki.build import jit_compile, jit_compile_lib, clean_test from loki.frontend import available_frontends, OFP, OMNI, REGEX @@ -63,9 +62,7 @@ def test_routine_simple(tmp_path, frontend): assert routine.docstring[0].text == '! This is the docstring' assert routine.definitions == () - routine_args = [str(arg) for arg in routine.arguments] - assert routine_args in (['x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)'], - ['x', 'y', 'scalar', 'vector(1:x)', 'matrix(1:x, 1:y)']) # OMNI + assert routine.arguments == ('x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)') # Generate code, compile and load filepath = tmp_path/(f'routine_simple_{frontend}.f90') @@ -114,14 +111,11 @@ def test_routine_arguments(tmp_path, frontend): """ routine = Subroutine.from_source(fcode, frontend=frontend) - routine_vars = [str(arg) for arg in routine.variables] - assert routine_vars in (['jprb', 'x', 'y', 'vector(x)', 'matrix(x, y)', - 'i', 'j', 'local_vector(x)', 'local_matrix(x, y)'], - ['jprb', 'x', 'y', 'vector(1:x)', 'matrix(1:x, 1:y)', - 'i', 'j', 'local_vector(1:x)', 'local_matrix(1:x, 1:y)']) - routine_args = [str(arg) for arg in routine.arguments] - assert routine_args in (['x', 'y', 'vector(x)', 'matrix(x, y)'], - ['x', 'y', 'vector(1:x)', 'matrix(1:x, 1:y)']) + assert routine.variables == ( + 'jprb', 'x', 'y', 'vector(x)', 'matrix(x, y)', + 'i', 'j', 'local_vector(x)', 'local_matrix(x, y)' + ) + assert routine.arguments == ('x', 'y', 'vector(x)', 'matrix(x, y)') # Generate code, compile and load filepath = tmp_path/(f'routine_arguments_{frontend}.f90') @@ -165,9 +159,7 @@ def test_routine_arguments_multiline(tmp_path, frontend): # Test the internals of the subroutine routine = Subroutine.from_source(fcode, frontend=frontend) - routine_args = [str(arg) for arg in routine.arguments] - assert routine_args in (['x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)'], - ['x', 'y', 'scalar', 'vector(1:x)', 'matrix(1:x, 1:y)']) + assert routine.arguments == ('x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)') # Generate code, compile and load filepath = tmp_path/(f'routine_arguments_multiline_{frontend}.f90') @@ -202,9 +194,7 @@ def test_routine_arguments_order(frontend): end subroutine routine_arguments_order """ routine = Subroutine.from_source(fcode, frontend=frontend) - routine_args = [str(arg) for arg in routine.arguments] - assert routine_args in (['x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)'], - ['x', 'y', 'scalar', 'vector(1:x)', 'matrix(1:x, 1:y)']) + assert routine.arguments == ('x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)') @pytest.mark.parametrize('frontend', available_frontends()) @@ -221,9 +211,7 @@ def test_routine_arguments_add_remove(frontend): end subroutine routine_arguments_add_remove """ routine = Subroutine.from_source(fcode, frontend=frontend) - routine_args = [str(arg) for arg in routine.arguments] - assert routine_args in (['x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)'], - ['x', 'y', 'scalar', 'vector(1:x)', 'matrix(1:x, 1:y)']) + assert routine.arguments == ('x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)') # Create a new set of variables and add to local routine variables x = routine.variables[1] # That's the symbol for variable 'x' @@ -237,7 +225,6 @@ def test_routine_arguments_add_remove(frontend): routine_args = [str(arg) for arg in routine.arguments] assert routine_args in ( ['x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)', 'a', 'b(x)', 'c'], - ['x', 'y', 'scalar', 'vector(1:x)', 'matrix(1:x, 1:y)', 'a', 'b(x)', 'c', ] ) if frontend == OMNI: assert fgen(routine.spec).lower() == """ @@ -246,8 +233,8 @@ def test_routine_arguments_add_remove(frontend): integer, intent(in) :: x integer, intent(in) :: y real(kind=selected_real_kind(13, 300)), intent(in) :: scalar -real(kind=selected_real_kind(13, 300)), intent(inout) :: vector(1:x) -real(kind=selected_real_kind(13, 300)), intent(inout) :: matrix(1:x, 1:y) +real(kind=selected_real_kind(13, 300)), intent(inout) :: vector(x) +real(kind=selected_real_kind(13, 300)), intent(inout) :: matrix(x, y) real(kind=selected_real_kind(13, 300)), intent(in) :: a real(kind=selected_real_kind(13, 300)), intent(in) :: b(x) integer, intent(in) :: c @@ -265,13 +252,12 @@ def test_routine_arguments_add_remove(frontend): # Remove a select number of arguments routine.arguments = [arg for arg in routine.arguments if 'x' not in str(arg)] - routine_args = [str(arg) for arg in routine.arguments] - assert routine_args == ['y', 'scalar', 'a', 'c', ] + assert routine.arguments == ('y', 'scalar', 'a', 'c' ) # Check that removed args still exist as variables routine_vars = [str(arg) for arg in routine.variables] - assert 'vector(x)' in routine_vars or 'vector(1:x)' in routine_vars - assert 'matrix(x, y)' in routine_vars or 'matrix(1:x, 1:y)' in routine_vars + assert 'vector(x)' in routine_vars + assert 'matrix(x, y)' in routine_vars assert 'b(x)' in routine_vars @@ -303,10 +289,9 @@ def test_routine_variables_local(tmp_path, frontend): # Test the internals of the subroutine routine = Subroutine.from_source(fcode, frontend=frontend) - routine_vars = [str(arg) for arg in routine.variables] - assert routine_vars in ( - ['jprb', 'x', 'y', 'maximum', 'i', 'j', 'vector(x)', 'matrix(x, y)'], - ['jprb', 'x', 'y', 'maximum', 'i', 'j', 'vector(1:x)', 'matrix(1:x, 1:y)']) + assert routine.variables == ( + 'jprb', 'x', 'y', 'maximum', 'i', 'j', 'vector(x)', 'matrix(x, y)' + ) # Generate code, compile and load filepath = tmp_path/(f'routine_variables_local_{frontend}.f90') @@ -358,16 +343,12 @@ def test_routine_variable_caching(frontend): # Test the internals of the subroutine routine = Subroutine.from_source(fcode_real, frontend=frontend) - routine_args = [str(arg) for arg in routine.arguments] - assert routine_args in (['x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)'], - ['x', 'y', 'scalar', 'vector(1:x)', 'matrix(1:x, 1:y)']) + assert routine.arguments == ('x', 'y', 'scalar', 'vector(x)', 'matrix(x, y)') assert routine.arguments[2].type.dtype == BasicType.REAL assert routine.arguments[3].type.dtype == BasicType.REAL routine = Subroutine.from_source(fcode_int, frontend=frontend) - routine_args = [str(arg) for arg in routine.arguments] - assert routine_args in (['x', 'y', 'scalar', 'vector(y)', 'matrix(x, y)'], - ['x', 'y', 'scalar', 'vector(1:y)', 'matrix(1:x, 1:y)']) + assert routine.arguments == ('x', 'y', 'scalar', 'vector(y)', 'matrix(x, y)') # Ensure that the types in the second routine have been picked up assert routine.arguments[2].type.dtype == BasicType.INTEGER assert routine.arguments[3].type.dtype == BasicType.INTEGER @@ -388,11 +369,7 @@ def test_routine_variables_add_remove(frontend): end subroutine routine_variables_add_remove """ routine = Subroutine.from_source(fcode, frontend=frontend) - routine_vars = [str(arg) for arg in routine.variables] - assert routine_vars in ( - ['jprb', 'x', 'y', 'maximum', 'vector(x)', 'matrix(x, y)'], - ['jprb', 'x', 'y', 'maximum', 'vector(1:x)', 'matrix(1:x, 1:y)'] - ) + assert routine.variables == ('jprb', 'x', 'y', 'maximum', 'vector(x)', 'matrix(x, y)') # Create a new set of variables and add to local routine variables x = routine.variable_map['x'] # That's the symbol for variable 'x' @@ -412,8 +389,8 @@ def test_routine_variables_add_remove(frontend): integer, intent(in) :: x integer, intent(in) :: y real(kind=selected_real_kind(13, 300)), intent(out) :: maximum -real(kind=selected_real_kind(13, 300)), intent(inout) :: vector(1:x) -real(kind=selected_real_kind(13, 300)) :: matrix(1:x, 1:y) +real(kind=selected_real_kind(13, 300)), intent(inout) :: vector(x) +real(kind=selected_real_kind(13, 300)) :: matrix(x, y) real(kind=jprb) :: a real(kind=jprb) :: b(x) integer :: c @@ -434,16 +411,11 @@ def test_routine_variables_add_remove(frontend): # Now remove the `maximum` variable and make sure it's gone routine.variables = [v for v in routine.variables if v.name != 'maximum'] assert 'maximum' not in fgen(routine.spec).lower() - routine_vars = [str(arg) for arg in routine.variables] - assert routine_vars in ( - ['jprb', 'x', 'y', 'vector(x)', 'matrix(x, y)', 'a', 'b(x)', 'c'], - ['jprb', 'x', 'y', 'vector(1:x)', 'matrix(1:x, 1:y)', 'a', 'b(x)', 'c'] + assert routine.variables == ( + 'jprb', 'x', 'y', 'vector(x)', 'matrix(x, y)', 'a', 'b(x)', 'c' ) # Ensure `maximum` has been removed from arguments, but they are otherwise unharmed - assert [str(arg) for arg in routine.arguments] in ( - ['x', 'y', 'vector(x)'], - ['x', 'y', 'vector(1:x)'] - ) + assert routine.arguments == ('x', 'y', 'vector(x)') @pytest.mark.parametrize('frontend', available_frontends()) @@ -506,7 +478,7 @@ def test_routine_variables_dim_shapes(frontend): ! Simple variable assignments with non-trivial sizes and indices integer, parameter :: jprb = selected_real_kind(13,300) real(kind=jprb), allocatable, intent(out) :: v3(:) - real(kind=jprb), intent(out) :: v4(v1,v2), v5(1:v1,v2-1) + real(kind=jprb), intent(out) :: v4(v1,v2), v5(0:v1,v2-1) integer, intent(in) :: v1, v2 allocate(v3(v1)) @@ -518,14 +490,11 @@ def test_routine_variables_dim_shapes(frontend): """ # TODO: Need a named subroutine lookup routine = Subroutine.from_source(fcode, frontend=frontend) - routine_args = [fexprgen(arg) for arg in routine.arguments] - assert routine_args in (['v1', 'v2', 'v3(:)', 'v4(v1, v2)', 'v5(1:v1, v2 - 1)'], - ['v1', 'v2', 'v3(:)', 'v4(1:v1, 1:v2)', 'v5(1:v1, 1:v2 - 1)']) + assert routine.arguments == ('v1', 'v2', 'v3(:)', 'v4(v1, v2)', 'v5(0:v1, v2 - 1)') # Make sure variable/argument shapes on the routine work shapes = [fexprgen(v.shape) for v in routine.arguments if isinstance(v, Array)] - assert shapes in (['(v1,)', '(v1, v2)', '(1:v1, v2 - 1)'], - ['(v1,)', '(1:v1, 1:v2)', '(1:v1, 1:v2 - 1)']) + assert shapes == ['(v1,)', '(v1, v2)', '(0:v1, v2 - 1)'] # Ensure that all spec variables (including dimension symbols) are scoped correctly spec_vars = [v for v in FindVariables(unique=False).visit(routine.spec) if v.name.lower() != 'selected_real_kind'] @@ -535,8 +504,7 @@ def test_routine_variables_dim_shapes(frontend): # Ensure shapes of body variables are ok b_shapes = [fexprgen(v.shape) for v in FindVariables(unique=False).visit(routine.body) if isinstance(v, Array)] - assert b_shapes in (['(v1,)', '(v1,)', '(v1, v2)', '(1:v1, v2 - 1)'], - ['(v1,)', '(v1,)', '(1:v1, 1:v2)', '(1:v1, 1:v2 - 1)']) + assert b_shapes == ['(v1,)', '(v1,)', '(v1, v2)', '(0:v1, v2 - 1)'] @pytest.mark.parametrize('frontend', available_frontends()) @@ -567,16 +535,16 @@ def test_routine_variables_shape_propagation(tmp_path, header_path, frontend): # TODO: The string comparison tmp_path is due to the fact that shapes are actually # `RangeIndex(upper=Scalar)` objects, instead of the raw dimension variables. # This needs some more thorough conceptualisation of dimensions and indices! - assert fexprgen(routine.arguments[3].shape) in ['(x,)', '(1:x,)'] - assert fexprgen(routine.arguments[4].shape) in ['(x, y)', '(1:x, 1:y)'] + assert fexprgen(routine.arguments[3].shape) == '(x,)' + assert fexprgen(routine.arguments[4].shape) == '(x, y)' # Verify that all variable instances have type and shape information variables = FindVariables().visit(routine.body) assert all(v.shape is not None for v in variables if isinstance(v, Array)) vmap = {v.name: v for v in variables} - assert fexprgen(vmap['vector'].shape) in ['(x,)', '(1:x,)'] - assert fexprgen(vmap['matrix'].shape) in ['(x, y)', '(1:x, 1:y)'] + assert fexprgen(vmap['vector'].shape) == '(x,)' + assert fexprgen(vmap['matrix'].shape) == '(x, y)' # Parse kernel with external typedefs to test shape inferred from # external derived type definition @@ -612,8 +580,8 @@ def test_routine_variables_shape_propagation(tmp_path, header_path, frontend): # Verify shape info from imported derived type is propagated vmap = {v.name: v for v in variables} - assert fexprgen(vmap['item%vector'].shape) in ['(3,)', '(1:3,)'] - assert fexprgen(vmap['item%matrix'].shape) in ['(3, 3)', '(1:3, 1:3)'] + assert fexprgen(vmap['item%vector'].shape) == '(3,)' + assert fexprgen(vmap['item%matrix'].shape) == '(3, 3)' @pytest.mark.parametrize('frontend', available_frontends(xfail=[(OMNI, 'OMNI does not like Loki pragmas, yet!')])) @@ -782,8 +750,8 @@ def test_routine_call_arrays(header_path, frontend, tmp_path): assert isinstance(call.arguments[3], Array) assert isinstance(call.arguments[4], Array) - assert fexprgen(call.arguments[2].shape) in ['(x,)', '(1:x,)'] - assert fexprgen(call.arguments[3].shape) in ['(x, y)', '(1:x, 1:y)'] + assert fexprgen(call.arguments[2].shape) == '(x,)' + assert fexprgen(call.arguments[3].shape) == '(x, y)' # assert fexprgen(call.arguments[4].shape) in ['(3, 3)', '(1:3, 1:3)'] assert fgen(call) == 'CALL routine_call_callee(x, y, vector, matrix, item%matrix)' @@ -1275,7 +1243,7 @@ def test_subroutine_interface(tmp_path, frontend, header_path): IMPLICIT NONE INTEGER, INTENT(IN) :: in1 INTEGER, INTENT(IN) :: in2 - REAL(KIND=selected_real_kind(13, 300)), INTENT(IN) :: in3(1:in1, 1:in2) + REAL(KIND=selected_real_kind(13, 300)), INTENT(IN) :: in3(in1, in2) REAL(KIND=selected_real_kind(13, 300)), INTENT(OUT) :: out1 REAL(KIND=selected_real_kind(13, 300)), INTENT(OUT) :: out2 END SUBROUTINE test_subroutine_interface @@ -1787,8 +1755,6 @@ def test_subroutine_lazy_arguments_incomplete1(frontend): assert all(isinstance(arg, DeferredTypeSymbol) for arg in routine.arguments) routine.make_complete(frontend=frontend) - if frontend == OMNI: - normalize_range_indexing(routine) assert not routine._incomplete assert routine.arguments == ('n', 'a(n)', 'b(n)', 'd(n)') assert routine.argnames == ['n', 'a', 'b', 'd'] @@ -1797,7 +1763,6 @@ def test_subroutine_lazy_arguments_incomplete1(frontend): assert all(isinstance(arg, Array) for arg in routine.arguments[1:]) - @pytest.mark.parametrize('frontend', available_frontends()) def test_subroutine_lazy_arguments_incomplete2(frontend): """ @@ -1879,8 +1844,6 @@ def test_subroutine_lazy_arguments_incomplete2(frontend): assert all(isinstance(arg, DeferredTypeSymbol) for arg in routine.arguments) routine.make_complete(frontend=frontend) - if frontend == OMNI: - normalize_range_indexing(routine) assert not routine._incomplete assert routine.arguments == argnames_with_dim assert [arg.upper() for arg in routine.argnames] == [arg.upper() for arg in argnames] diff --git a/loki/transformations/single_column/tests/test_scc.py b/loki/transformations/single_column/tests/test_scc.py index 0059a27b7..ffb0fe44b 100644 --- a/loki/transformations/single_column/tests/test_scc.py +++ b/loki/transformations/single_column/tests/test_scc.py @@ -882,8 +882,7 @@ def test_scc_demotion_parameter(frontend, horizontal, tmp_path): assert len(routine.symbol_map['work'].shape) == 2 if frontend == OMNI: - assert routine.symbol_map['work'].shape == (RangeIndex(children=(IntLiteral(1), IntLiteral(36))), - IntLiteral(2)) + assert routine.symbol_map['work'].shape == (IntLiteral(36), IntLiteral(2)) else: assert routine.symbol_map['work'].shape == ('nang_param', IntLiteral(2)) diff --git a/loki/transformations/tests/test_array_indexing.py b/loki/transformations/tests/test_array_indexing.py index 42112e7ad..7d06088f6 100644 --- a/loki/transformations/tests/test_array_indexing.py +++ b/loki/transformations/tests/test_array_indexing.py @@ -16,10 +16,10 @@ from loki.ir import FindNodes, CallStatement, Loop from loki.transformations.array_indexing import ( - promote_variables, demote_variables, normalize_range_indexing, - invert_array_indices, flatten_arrays, - normalize_array_shape_and_access, shift_to_zero_indexing, - resolve_vector_notation, LowerConstantArrayIndices + promote_variables, demote_variables, invert_array_indices, + flatten_arrays, normalize_array_shape_and_access, + shift_to_zero_indexing, resolve_vector_notation, + LowerConstantArrayIndices ) from loki.transformations.transpile import FortranCTransformation @@ -103,7 +103,6 @@ def test_transform_promote_variables(tmp_path, frontend): end subroutine transform_promote_variables """.strip() routine = Subroutine.from_source(fcode, frontend=frontend) - normalize_range_indexing(routine) # Fix OMNI nonsense # Test the original implementation filepath = tmp_path/(f'{routine.name}_{frontend}.f90') @@ -197,7 +196,6 @@ def test_transform_demote_variables(tmp_path, frontend): end subroutine transform_demote_variables """.strip() routine = Subroutine.from_source(fcode, frontend=frontend) - normalize_range_indexing(routine) # Fix OMNI nonsense # Test the original implementation filepath = tmp_path/(f'{routine.name}_{frontend}.f90') @@ -273,7 +271,6 @@ def test_transform_demote_dimension_arguments(tmp_path, frontend): end subroutine transform_demote_dimension_arguments """ routine = Subroutine.from_source(fcode, frontend=frontend) - normalize_range_indexing(routine) # Fix OMNI nonsense # Test the original implementation filepath = tmp_path/(f'{routine.name}_{frontend}.f90') @@ -409,10 +406,8 @@ def validate_routine(routine): l3 = 4 l4 = 5 module = Module.from_source(fcode, frontend=frontend, xmods=[tmp_path]) - for routine in module.routines: - normalize_range_indexing(routine) # Fix OMNI nonsense filepath = tmp_path/(f'norm_arr_shape_access_{frontend}.f90') - # compile and test "original" module/function + # compile and test "original" module/function mod = jit_compile(module, filepath=filepath, objname='norm_arr_shape_access_mod') function = getattr(mod, 'norm_arr_shape_access') orig_x1, orig_x2, orig_x3, orig_x4, orig_assumed_x1 = init_arguments(l1, l2, l3, l4) @@ -505,7 +500,6 @@ def validate_routine(routine): l4 = 5 # Test the original implementation routine = Subroutine.from_source(fcode, frontend=frontend) - normalize_range_indexing(routine) # Fix OMNI nonsense filepath = tmp_path/(f'{routine.name}_{start_index}_{frontend}.f90') function = jit_compile(routine, filepath=filepath, objname=routine.name) orig_x1, orig_x2, orig_x3, orig_x4 = init_arguments(l1, l2, l3, l4) @@ -515,7 +509,6 @@ def validate_routine(routine): # Test flattening order='F' f_routine = Subroutine.from_source(fcode, frontend=frontend) normalize_array_shape_and_access(f_routine) - normalize_range_indexing(f_routine) # Fix OMNI nonsense flatten_arrays(routine=f_routine, order='F', start_index=1) filepath = tmp_path/(f'{f_routine.name}_{start_index}_flattened_F_{frontend}.f90') function = jit_compile(f_routine, filepath=filepath, objname=routine.name) @@ -532,7 +525,6 @@ def validate_routine(routine): # Test flattening order='C' c_routine = Subroutine.from_source(fcode, frontend=frontend) normalize_array_shape_and_access(c_routine) - normalize_range_indexing(c_routine) # Fix OMNI nonsense invert_array_indices(c_routine) flatten_arrays(routine=c_routine, order='C', start_index=1) filepath = tmp_path/(f'{c_routine.name}_{start_index}_flattened_C_{frontend}.f90') @@ -696,9 +688,6 @@ def validate_routine(routine): ref_function(nlon, nlev, a_ref, b_ref) builder.clean() - normalize_range_indexing(driver) - normalize_range_indexing(kernel) - # flatten all the arrays in the kernel and driver flatten_arrays(routine=kernel, order='F', start_index=1) flatten_arrays(routine=driver, order='F', start_index=1) diff --git a/loki/transformations/tests/test_data_offload.py b/loki/transformations/tests/test_data_offload.py index e57d46fa3..81dad4dee 100644 --- a/loki/transformations/tests/test_data_offload.py +++ b/loki/transformations/tests/test_data_offload.py @@ -379,10 +379,10 @@ def test_global_variable_analysis(frontend, key, config, global_variable_analysi # Validate the analysis trafo_data - # OMNI handles array indices and parameters differently + # OMNI handles array indices and parameters differently if frontend == OMNI: - nfld_dim = '1:3' - nval_dim = '1:5' + nfld_dim = '3' + nval_dim = '5' nfld_data = set() nval_data = set() else: @@ -449,10 +449,10 @@ def test_global_variable_offload(frontend, key, config, global_variable_analysis 'driver': {'role': 'driver'} } - # OMNI handles array indices and parameters differently + # OMNI handles array indices and parameters differently if frontend == OMNI: - nfld_dim = '1:3' - nval_dim = '1:5' + nfld_dim = '3' + nval_dim = '5' else: nfld_dim = 'nfld' nval_dim = 'nval' diff --git a/loki/transformations/tests/test_extract.py b/loki/transformations/tests/test_extract.py index b8e9de8ff..0a31aa1d3 100644 --- a/loki/transformations/tests/test_extract.py +++ b/loki/transformations/tests/test_extract.py @@ -110,10 +110,7 @@ def test_extract_contained_procedures_basic_array(frontend): inner = routines[0] outer = src.routines[0] assert 'x' in inner.arguments - if frontend == OMNI: - assert 'arr(1:3)' in inner.arguments - else: - assert 'arr(3)' in inner.arguments + assert 'arr(3)' in inner.arguments call = FindNodes(CallStatement).visit(outer.body)[0] kwargdict = dict(call.kwarguments) @@ -562,8 +559,9 @@ def test_extract_contained_procedures_basic_scalar_function(frontend): call = list(FindInlineCalls().visit(outer.body))[0] assert 'x' in call.kw_parameters -@pytest.mark.parametrize('frontend', - available_frontends(xfail=(OFP, "ofp fails for unknown reason, likely frontend issue"))) +@pytest.mark.parametrize( + 'frontend', available_frontends(skip=(OFP, "ofp fails for unknown reason, likely frontend issue")) +) def test_extract_contained_procedures_basic_scalar_function_both(frontend): """ Basic test for scalars highlighting that the outer and inner procedure may be functions. diff --git a/loki/transformations/tests/test_hoist_variables.py b/loki/transformations/tests/test_hoist_variables.py index 99aacc04f..85aacffed 100644 --- a/loki/transformations/tests/test_hoist_variables.py +++ b/loki/transformations/tests/test_hoist_variables.py @@ -13,8 +13,7 @@ import numpy as np from loki import ( - Scheduler, SchedulerConfig, is_iterable, - normalize_range_indexing, FindInlineCalls + Scheduler, SchedulerConfig, is_iterable, FindInlineCalls ) from loki.build import jit_compile_lib, Builder from loki.frontend import available_frontends, OMNI @@ -636,11 +635,6 @@ def test_hoist_mixed_variable_declarations(tmp_path, frontend, config): scheduler = Scheduler( paths=[tmp_path], config=SchedulerConfig.from_dict(config), frontend=frontend, xmods=[tmp_path] ) - - if frontend == OMNI: - for item in scheduler.items: - normalize_range_indexing(item.ir) - scheduler.process(transformation=HoistTemporaryArraysAnalysis(dim_vars=('klev',))) scheduler.process(transformation=HoistTemporaryArraysTransformationAllocatable()) diff --git a/loki/transformations/tests/test_inline.py b/loki/transformations/tests/test_inline.py index 8c0669317..6c8266b57 100644 --- a/loki/transformations/tests/test_inline.py +++ b/loki/transformations/tests/test_inline.py @@ -1191,9 +1191,9 @@ def test_inline_marked_subroutines_declarations(frontend, tmp_path): inline_marked_subroutines(routine=outer, adjust_imports=True) # Check that all declarations are using the ``bnds`` symbol - assert outer.symbols[0] == 'a(1:bnds%end)' if frontend == OMNI else 'a(bnds%end)' - assert outer.symbols[2] == 'b(1:bnds%end)' if frontend == OMNI else 'b(bnds%end)' - assert outer.symbols[3] == 'd(1:bnds%end)' if frontend == OMNI else 'd(bnds%end)' + assert outer.symbols[0] == 'a(bnds%end)' + assert outer.symbols[2] == 'b(bnds%end)' + assert outer.symbols[3] == 'd(bnds%end)' assert all( a.shape == ('bnds%end',) for a in outer.symbols if isinstance(a, sym.Array) ) diff --git a/loki/transformations/tests/test_parametrise.py b/loki/transformations/tests/test_parametrise.py index ff7d90134..484b5bcd3 100644 --- a/loki/transformations/tests/test_parametrise.py +++ b/loki/transformations/tests/test_parametrise.py @@ -15,7 +15,7 @@ from loki import Scheduler, fgen from loki.build import jit_compile from loki.expression import symbols as sym -from loki.frontend import available_frontends, OMNI +from loki.frontend import available_frontends from loki.ir import nodes as ir, FindNodes from loki.transformations.parametrise import ParametriseTransformation @@ -279,56 +279,30 @@ def test_parametrise_simple_replace_by_value(tmp_path, testdir, frontend, config check_arguments_and_parameter(scheduler=scheduler, subroutine_arguments=subroutine_arguments, call_arguments=call_arguments, parameter_variables=parameter_variables) - if frontend == OMNI: - routine_spec_str = fgen(scheduler['parametrise#driver'].ir.spec) - assert f'c(1:{a}, 1:{b})' in routine_spec_str - assert f'd(1:{b})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#another_driver'].ir.spec) - assert f'c(1:{a}, 1:{b})' in routine_spec_str - assert f'x(1:{a})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#kernel1'].ir.spec) - assert f'c(1:{a}, 1:{b})' in routine_spec_str - assert f'x(1:{a})' in routine_spec_str - assert f'y(1:{a}, 1:{b})' in routine_spec_str - assert f'k1_tmp(1:{a}, 1:{b})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#kernel2'].ir.spec) - assert f'd(1:{b})' in routine_spec_str - assert f'x(1:{a})' in routine_spec_str - assert f'k2_tmp(1:{a}, 1:{a})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#device1'].ir.spec) - assert f'd(1:{b})' in routine_spec_str - assert f'x(1:{a})' in routine_spec_str - assert f'y(1:{a}, 1:{a})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#device2'].ir.spec) - assert f'd(1:{b})' in routine_spec_str - assert f'x(1:{a})' in routine_spec_str - assert f'z(1:{b})' in routine_spec_str - assert f'd2_tmp(1:{b})' in routine_spec_str - else: - routine_spec_str = fgen(scheduler['parametrise#driver'].ir.spec) - assert f'c({a}, {b})' in routine_spec_str - assert f'd({b})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#another_driver'].ir.spec) - assert f'c({a}, {b})' in routine_spec_str - assert f'x({a})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#kernel1'].ir.spec) - assert f'c({a}, {b})' in routine_spec_str - assert f'x({a})' in routine_spec_str - assert f'y({a}, {b})' in routine_spec_str - assert f'k1_tmp({a}, {b})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#kernel2'].ir.spec) - assert f'd({b})' in routine_spec_str - assert f'x({a})' in routine_spec_str - assert f'k2_tmp({a}, {a})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#device1'].ir.spec) - assert f'd({b})' in routine_spec_str - assert f'x({a})' in routine_spec_str - assert f'y({a}, {a})' in routine_spec_str - routine_spec_str = fgen(scheduler['parametrise#device2'].ir.spec) - assert f'd({b})' in routine_spec_str - assert f'x({a})' in routine_spec_str - assert f'z({b})' in routine_spec_str - assert f'd2_tmp({b})' in routine_spec_str + routine_spec_str = fgen(scheduler['parametrise#driver'].ir.spec) + assert f'c({a}, {b})' in routine_spec_str + assert f'd({b})' in routine_spec_str + routine_spec_str = fgen(scheduler['parametrise#another_driver'].ir.spec) + assert f'c({a}, {b})' in routine_spec_str + assert f'x({a})' in routine_spec_str + routine_spec_str = fgen(scheduler['parametrise#kernel1'].ir.spec) + assert f'c({a}, {b})' in routine_spec_str + assert f'x({a})' in routine_spec_str + assert f'y({a}, {b})' in routine_spec_str + assert f'k1_tmp({a}, {b})' in routine_spec_str + routine_spec_str = fgen(scheduler['parametrise#kernel2'].ir.spec) + assert f'd({b})' in routine_spec_str + assert f'x({a})' in routine_spec_str + assert f'k2_tmp({a}, {a})' in routine_spec_str + routine_spec_str = fgen(scheduler['parametrise#device1'].ir.spec) + assert f'd({b})' in routine_spec_str + assert f'x({a})' in routine_spec_str + assert f'y({a}, {a})' in routine_spec_str + routine_spec_str = fgen(scheduler['parametrise#device2'].ir.spec) + assert f'd({b})' in routine_spec_str + assert f'x({a})' in routine_spec_str + assert f'z({b})' in routine_spec_str + assert f'd2_tmp({b})' in routine_spec_str compile_and_test(scheduler=scheduler, tmp_path=tmp_path, a=a, b=b) diff --git a/loki/transformations/tests/test_pool_allocator.py b/loki/transformations/tests/test_pool_allocator.py index 16d847a06..84f41d330 100644 --- a/loki/transformations/tests/test_pool_allocator.py +++ b/loki/transformations/tests/test_pool_allocator.py @@ -7,7 +7,7 @@ import pytest -from loki import Dimension, normalize_range_indexing +from loki import Dimension from loki.batch import Scheduler, SchedulerConfig, SFilter, ProcedureItem from loki.expression import ( FindVariables, FindInlineCalls, InlineCall, simplify @@ -257,10 +257,6 @@ def test_pool_allocator_temporaries(tmp_path, frontend, generate_driver_stack, b frontend=frontend, xmods=[tmp_path] ) - if frontend == OMNI: - for item in SFilter(scheduler.sgraph, item_filter=ProcedureItem): - normalize_range_indexing(item.ir) - transformation = TemporariesPoolAllocatorTransformation( block_dim=block_dim, check_bounds=check_bounds, cray_ptr_loc_rhs=cray_ptr_loc_rhs @@ -602,9 +598,6 @@ def test_pool_allocator_temporaries_kernel_sequence(tmp_path, frontend, block_di paths=[tmp_path], config=SchedulerConfig.from_dict(config), frontend=frontend, xmods=[tmp_path] ) - if frontend == OMNI: - for item in SFilter(scheduler.sgraph, item_filter=ProcedureItem): - normalize_range_indexing(item.ir) transformation = TemporariesPoolAllocatorTransformation( block_dim=block_dim, directive=directive, cray_ptr_loc_rhs=cray_ptr_loc_rhs @@ -902,9 +895,6 @@ def test_pool_allocator_temporaries_kernel_nested(tmp_path, frontend, block_dim, paths=[tmp_path], config=SchedulerConfig.from_dict(config), frontend=frontend, xmods=[tmp_path] ) - if frontend == OMNI: - for item in SFilter(scheduler.sgraph, item_filter=ProcedureItem): - normalize_range_indexing(item.ir) transformation = TemporariesPoolAllocatorTransformation(block_dim=block_dim, directive=directive, cray_ptr_loc_rhs=cray_ptr_loc_rhs) @@ -1150,9 +1140,6 @@ def test_pool_allocator_more_call_checks(tmp_path, frontend, block_dim, caplog, scheduler = Scheduler( paths=[tmp_path], config=SchedulerConfig.from_dict(config), frontend=frontend, xmods=[tmp_path] ) - if frontend == OMNI: - for item in SFilter(scheduler.sgraph, item_filter=ProcedureItem): - normalize_range_indexing(item.ir) transformation = TemporariesPoolAllocatorTransformation(block_dim=block_dim, cray_ptr_loc_rhs=cray_ptr_loc_rhs) scheduler.process(transformation=transformation) @@ -1326,11 +1313,6 @@ def test_pool_allocator_args_vs_kwargs(tmp_path, frontend, block_dim_alt, cray_p frontend=frontend, xmods=[tmp_path] ) - if frontend == OMNI: - for item in scheduler.items: - if isinstance(item, ProcedureItem): - normalize_range_indexing(item.ir) - transformation = TemporariesPoolAllocatorTransformation(block_dim=block_dim_alt, cray_ptr_loc_rhs=cray_ptr_loc_rhs) scheduler.process(transformation=transformation) diff --git a/loki/transformations/tests/test_raw_stack_allocator.py b/loki/transformations/tests/test_raw_stack_allocator.py index 95814e78d..fc9ed0822 100644 --- a/loki/transformations/tests/test_raw_stack_allocator.py +++ b/loki/transformations/tests/test_raw_stack_allocator.py @@ -16,7 +16,6 @@ from loki.sourcefile import Sourcefile from loki.types import BasicType -from loki.transformations.array_indexing import normalize_range_indexing from loki.transformations.raw_stack_allocator import TemporariesRawStackTransformation @@ -264,11 +263,6 @@ def test_raw_stack_allocator_temporaries(frontend, block_dim, horizontal, direct scheduler = Scheduler(paths=[tmp_path], config=SchedulerConfig.from_dict(config), frontend=frontend, definitions=definitions, xmods=[tmp_path]) - if frontend == OMNI: - for item in scheduler.items: - if isinstance(item, ProcedureItem): - normalize_range_indexing(item.ir) - transformation = TemporariesRawStackTransformation(block_dim=block_dim, horizontal=horizontal, directive=directive) scheduler.process(transformation=transformation) diff --git a/loki/transformations/tests/test_transform_derived_types.py b/loki/transformations/tests/test_transform_derived_types.py index 29ff56b00..e1acb48ae 100644 --- a/loki/transformations/tests/test_transform_derived_types.py +++ b/loki/transformations/tests/test_transform_derived_types.py @@ -325,10 +325,7 @@ def test_transform_derived_type_arguments_expansion(frontend, tmp_path): 'm', 'n', 't_io%a', 't_io%b', 't_in(i)%a', 't_in(i)%b', 't_out(i)%a', 't_out(i)%b' ) - if frontend == OMNI: - kernel_args = ('m', 'n', 'p_a(1:n)', 'p_b(1:m, 1:n)', 'q_a(:)', 'q_b(:, :)', 'r_a(:)', 'r_b(:, :)') - else: - kernel_args = ('m', 'n', 'P_a(n)', 'P_b(m, n)', 'Q_a(:)', 'Q_b(:, :)', 'R_a(:)', 'R_b(:, :)') + kernel_args = ('m', 'n', 'P_a(n)', 'P_b(m, n)', 'Q_a(:)', 'Q_b(:, :)', 'R_a(:)', 'R_b(:, :)') call = FindNodes(CallStatement).visit(source['caller'].ir)[0] assert call.name == 'kernel' @@ -755,14 +752,9 @@ def test_transform_derived_type_arguments_expansion_nested(frontend, tmp_path): ) # Check arguments of kernel - if frontend == OMNI: - assert items['kernel'].ir.arguments == ( - 'm', 'n', 'p_a(1:n)', 'p_b(1:m, 1:n)', 'q_a(:)', 'q_b(:, :)', 'r_a(:)', 'r_b(:, :)', 'c' - ) - else: - assert items['kernel'].ir.arguments == ( - 'm', 'n', 'P_a(n)', 'P_b(m, n)', 'Q_a(:)', 'Q_b(:, :)', 'R_a(:)', 'R_b(:, :)', 'c' - ) + assert items['kernel'].ir.arguments == ( + 'm', 'n', 'P_a(n)', 'P_b(m, n)', 'Q_a(:)', 'Q_b(:, :)', 'R_a(:)', 'R_b(:, :)', 'c' + ) # Check call arguments in layer calls = FindNodes(CallStatement).visit(items['layer'].ir.ir) @@ -776,16 +768,10 @@ def test_transform_derived_type_arguments_expansion_nested(frontend, tmp_path): ) # Check arguments of layer - if frontend == OMNI: - assert items['layer'].ir.arguments == ( - 'm', 'n', 'p_a_a(:)', 'p_a_b(:, :)', 'p_b(1:5)', 'q_a_a(:)', 'q_a_b(:, :)', 'q_b(:)', 'q_constants(:)', - 'r_a_a(:)', 'r_a_b(:, :)', 'r_b(:)' - ) - else: - assert items['layer'].ir.arguments == ( - 'm', 'n', 'p_a_a(:)', 'p_a_b(:, :)', 'p_b(5)', 'q_a_a(:)', 'q_a_b(:, :)', 'q_b(:)', 'q_constants(:)', - 'r_a_a(:)', 'r_a_b(:, :)', 'r_b(:)' - ) + assert items['layer'].ir.arguments == ( + 'm', 'n', 'p_a_a(:)', 'p_a_b(:, :)', 'p_b(5)', 'q_a_a(:)', 'q_a_b(:, :)', 'q_b(:)', 'q_constants(:)', + 'r_a_a(:)', 'r_a_b(:, :)', 'r_b(:)' + ) # Check imports assert 'constants_type' in items['layer'].ir.imported_symbols diff --git a/loki/transformations/tests/test_transform_loop.py b/loki/transformations/tests/test_transform_loop.py index d102958d3..a2209f4c3 100644 --- a/loki/transformations/tests/test_transform_loop.py +++ b/loki/transformations/tests/test_transform_loop.py @@ -19,7 +19,6 @@ Assignment ) -from loki.transformations.array_indexing import normalize_range_indexing from loki.transformations.transform_loop import ( loop_interchange, loop_fusion, loop_fission, loop_unroll ) @@ -964,7 +963,6 @@ def test_transform_loop_fission_nested_promote(tmp_path, frontend): end subroutine transform_loop_fission_nested_promote """ routine = Subroutine.from_source(fcode, frontend=frontend) - normalize_range_indexing(routine) filepath = tmp_path/(f'{routine.name}_{frontend}.f90') function = jit_compile(routine, filepath=filepath, objname=routine.name) @@ -1277,10 +1275,7 @@ def test_transform_loop_fission_promote_array(tmp_path, frontend): assert len(loops) == 3 assert all(loop.bounds.start == '1' for loop in loops) assert sum(loop.bounds.stop == 'klev' for loop in loops) == 2 - if frontend == OMNI: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['1:klon', 'klev'] - else: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['klon', 'klev'] + assert routine.variable_map['zsupsat'].shape == ('klon', 'klev') fissioned_filepath = tmp_path/(f'{routine.name}_fissioned_{frontend}.f90') fissioned_function = jit_compile(routine, filepath=fissioned_filepath, objname=routine.name) @@ -1333,11 +1328,8 @@ def test_transform_loop_fission_promote_multiple(tmp_path, frontend): assert len(loops) == 3 assert all(loop.bounds.start == '1' for loop in loops) assert sum(loop.bounds.stop == 'klev' for loop in loops) == 2 - if frontend == OMNI: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['1:klon', 'klev'] - else: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['klon', 'klev'] - assert [str(d) for d in routine.variable_map['tmp'].shape] == ['klev'] + assert routine.variable_map['zsupsat'].shape == ('klon', 'klev') + assert routine.variable_map['tmp'].shape == ('klev',) fissioned_filepath = tmp_path/(f'{routine.name}_fissioned_{frontend}.f90') fissioned_function = jit_compile(routine, filepath=fissioned_filepath, objname=routine.name) @@ -1404,12 +1396,8 @@ def test_transform_loop_fission_multiple_promote(tmp_path, frontend): assert sum(loop.bounds.stop == 'klev' for loop in loops) == 4 assert sum(loop.bounds.stop == 'klon' for loop in loops) == 2 assert sum(loop.bounds.stop == 'nclv' for loop in loops) == 2 - if frontend == OMNI: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['1:klon', 'klev'] - assert [str(d) for d in routine.variable_map['zqxn'].shape] == ['1:klon', '1:nclv', 'klev'] - else: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['klon', 'klev'] - assert [str(d) for d in routine.variable_map['zqxn'].shape] == ['klon', 'nclv', 'klev'] + assert routine.variable_map['zsupsat'].shape == ('klon', 'klev') + assert routine.variable_map['zqxn'].shape == ('klon', 'nclv', 'klev') fissioned_filepath = tmp_path/(f'{routine.name}_fissioned_{frontend}.f90') fissioned_function = jit_compile(routine, filepath=fissioned_filepath, objname=routine.name) @@ -1465,11 +1453,8 @@ def test_transform_loop_fission_promote_read_after_write(tmp_path, frontend): assert len(loops) == 3 assert all(loop.bounds.start == '1' for loop in loops) assert sum(loop.bounds.stop == 'klev' for loop in loops) == 2 - if frontend == OMNI: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['1:klon', 'klev'] - else: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['klon', 'klev'] - assert [str(d) for d in routine.variable_map['tmp'].shape] == ['klev'] + assert routine.variable_map['zsupsat'].shape == ('klon', 'klev') + assert routine.variable_map['tmp'].shape == ('klev',) fissioned_filepath = tmp_path/(f'{routine.name}_fissioned_{frontend}.f90') fissioned_function = jit_compile(routine, filepath=fissioned_filepath, objname=routine.name) @@ -1537,12 +1522,8 @@ def test_transform_loop_fission_promote_multiple_read_after_write(tmp_path, fron assert sum(loop.bounds.stop == 'klev' for loop in loops) == 4 assert sum(loop.bounds.stop == 'klon' for loop in loops) == 2 assert sum(loop.bounds.stop == 'nclv' for loop in loops) == 2 - if frontend == OMNI: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['1:klon', 'klev'] - assert [str(d) for d in routine.variable_map['zqxn'].shape] == ['1:nclv', '1:klon', 'klev'] - else: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['klon', 'klev'] - assert [str(d) for d in routine.variable_map['zqxn'].shape] == ['nclv', 'klon', 'klev'] + assert routine.variable_map['zsupsat'].shape == ('klon', 'klev') + assert routine.variable_map['zqxn'].shape == ('nclv', 'klon', 'klev') fissioned_filepath = tmp_path/(f'{routine.name}_fissioned_{frontend}.f90') fissioned_function = jit_compile(routine, filepath=fissioned_filepath, objname=routine.name) @@ -1609,10 +1590,7 @@ def test_transform_loop_fusion_fission(tmp_path, frontend): assert all(loop.bounds.start == '1' for loop in loops) assert sum(loop.bounds.stop == 'klev' for loop in loops) == 2 assert sum(loop.bounds.stop == 'klon' for loop in loops) == 2 - if frontend == OMNI: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['1:klon', 'klev'] - else: - assert [str(d) for d in routine.variable_map['zsupsat'].shape] == ['klon', 'klev'] + assert routine.variable_map['zsupsat'].shape == ('klon', 'klev') fissioned_filepath = tmp_path/(f'{routine.name}_fissioned_{frontend}.f90') fissioned_function = jit_compile(routine, filepath=fissioned_filepath, objname=routine.name) diff --git a/loki/transformations/tests/test_transform_region.py b/loki/transformations/tests/test_transform_region.py index aa954ea2f..4ae233174 100644 --- a/loki/transformations/tests/test_transform_region.py +++ b/loki/transformations/tests/test_transform_region.py @@ -16,7 +16,6 @@ from loki.expression import symbols as sym from loki.frontend import available_frontends -from loki.transformations.array_indexing import normalize_range_indexing from loki.transformations.transform_region import ( region_hoist, region_to_call ) @@ -556,7 +555,6 @@ def test_transform_region_to_call_arrays(tmp_path, frontend): end subroutine reg_to_call_arr """ routine = Subroutine.from_source(fcode, frontend=frontend) - normalize_range_indexing(routine) filepath = tmp_path/(f'{routine.name}_{frontend}.f90') function = jit_compile(routine, filepath=filepath, objname=routine.name) @@ -646,7 +644,6 @@ def test_transform_region_to_call_imports(tmp_path, builder, frontend): """ ext_module = Module.from_source(fcode_module, frontend=frontend, xmods=[tmp_path]) module = Module.from_source(fcode, frontend=frontend, definitions=ext_module, xmods=[tmp_path]) - normalize_range_indexing(module.subroutines[0]) refname = f'ref_{module.name}_{frontend}' reference = jit_compile_lib([module, ext_module], path=tmp_path, name=refname, builder=builder) function = getattr(getattr(reference, module.name), module.subroutines[0].name) diff --git a/loki/transformations/tests/test_utilities.py b/loki/transformations/tests/test_utilities.py index 7dc3158e8..bd2af3b73 100644 --- a/loki/transformations/tests/test_utilities.py +++ b/loki/transformations/tests/test_utilities.py @@ -507,7 +507,7 @@ def test_transform_utilites_get_local_arrays(frontend, tmp_path): # Test for component arrays on arguments in spec locals = get_local_arrays(routine, routine.spec, unique=True) assert len(locals) == 1 - assert locals[0] == 'local(1:n)' if frontend == OMNI else 'local(n)' + assert locals[0] == 'local(n)' @pytest.mark.parametrize('frontend', available_frontends()) diff --git a/loki/transformations/transpile/fortran_maxeler.py b/loki/transformations/transpile/fortran_maxeler.py index 9b75ba9cb..859c25ee4 100644 --- a/loki/transformations/transpile/fortran_maxeler.py +++ b/loki/transformations/transpile/fortran_maxeler.py @@ -25,7 +25,7 @@ from loki.transformations.array_indexing import ( shift_to_zero_indexing, invert_array_indices, - resolve_vector_notation, normalize_range_indexing + resolve_vector_notation ) from loki.transformations.utilities import replace_intrinsics from loki.types import SymbolAttributes, BasicType, DerivedType @@ -191,7 +191,6 @@ def _generate_dfe_kernel(self, routine, **kwargs): # pylint: disable=unused-arg # Some vector notation sanitation resolve_vector_notation(max_kernel) - normalize_range_indexing(max_kernel) # Remove dataflow loops loop_map = {} diff --git a/loki/transformations/transpile/fortran_python.py b/loki/transformations/transpile/fortran_python.py index f2a769a67..078bc4a5a 100644 --- a/loki/transformations/transpile/fortran_python.py +++ b/loki/transformations/transpile/fortran_python.py @@ -16,7 +16,7 @@ from loki.sourcefile import Sourcefile from loki.transformations.array_indexing import ( - shift_to_zero_indexing, invert_array_indices, normalize_range_indexing + shift_to_zero_indexing, invert_array_indices ) from loki.transformations.sanitise import resolve_associates from loki.transformations.utilities import ( @@ -72,7 +72,6 @@ def transform_subroutine(self, routine, **kwargs): resolve_associates(routine) # Do some vector and indexing transformations - normalize_range_indexing(routine) if self.with_dace or self.invert_indices: invert_array_indices(routine) shift_to_zero_indexing(routine) diff --git a/loki/transformations/transpile/tests/test_transpile.py b/loki/transformations/transpile/tests/test_transpile.py index 69b226e2b..da76c70bb 100644 --- a/loki/transformations/transpile/tests/test_transpile.py +++ b/loki/transformations/transpile/tests/test_transpile.py @@ -15,7 +15,6 @@ from loki.frontend import available_frontends, OFP import loki.ir as ir -from loki.transformations.array_indexing import normalize_range_indexing from loki.transformations.transpile import FortranCTransformation @@ -98,7 +97,6 @@ def test_transpile_simple_loops(tmp_path, builder, frontend, use_c_ptr): # Generate reference code, compile run and verify routine = Subroutine.from_source(fcode, frontend=frontend) - normalize_range_indexing(routine) # Fix OMNI nonsense filepath = tmp_path/(f'simple_loops{"_c_ptr" if use_c_ptr else ""}_{frontend}.f90') function = jit_compile(routine, filepath=filepath, objname='simple_loops') @@ -197,7 +195,6 @@ def test_transpile_arguments(tmp_path, builder, frontend, use_c_ptr): # Generate reference code, compile run and verify routine = Subroutine.from_source(fcode, frontend=frontend) - normalize_range_indexing(routine) # Fix OMNI nonsense filepath = tmp_path/(f'transpile_arguments{"_c_ptr" if use_c_ptr else ""}_{frontend}.f90') function = jit_compile(routine, filepath=filepath, objname='transpile_arguments') a, b, c = function(n, array, array_io, a_io, b_io, c_io) diff --git a/scripts/loki_transform.py b/scripts/loki_transform.py index 6b5e8c02d..39b4f8c59 100644 --- a/scripts/loki_transform.py +++ b/scripts/loki_transform.py @@ -25,9 +25,7 @@ from loki.transformations.argument_shape import ( ArgumentArrayShapeAnalysis, ExplicitArgumentArrayShapeTransformation ) -from loki.transformations.array_indexing import ( - normalize_range_indexing, LowerConstantArrayIndices -) +from loki.transformations.array_indexing import LowerConstantArrayIndices from loki.transformations.build_system import ( DependencyTransformation, ModuleWrapTransformation, FileWriteTransformation ) @@ -243,15 +241,6 @@ def convert( scheduler.process(offload_transform) use_claw_offload = not offload_transform.has_data_regions - if frontend == Frontend.OMNI and mode in ['idem-stack', 'scc-stack']: - # To make the pool allocator size derivation work correctly, we need - # to normalize the 1:end-style index ranges that OMNI introduces - class NormalizeRangeIndexingTransformation(Transformation): - def transform_subroutine(self, routine, **kwargs): - normalize_range_indexing(routine) - - scheduler.process( NormalizeRangeIndexingTransformation() ) - if global_var_offload: scheduler.process(transformation=GlobalVariableAnalysis()) scheduler.process(transformation=GlobalVarOffloadTransformation())