Skip to content

Commit

Permalink
Frontend: [WIP] Remove OMNI special-casing for shape notation
Browse files Browse the repository at this point in the history
  • Loading branch information
mlange05 committed Aug 23, 2024
1 parent a675f22 commit 8837076
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 127 deletions.
13 changes: 4 additions & 9 deletions loki/expression/tests/test_expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(:)'

Expand Down Expand Up @@ -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)
Expand Down
9 changes: 3 additions & 6 deletions loki/frontend/tests/test_frontends.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,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):
Expand Down
6 changes: 3 additions & 3 deletions loki/tests/test_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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']
Expand Down
108 changes: 38 additions & 70 deletions loki/tests/test_subroutine.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,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')
Expand Down Expand Up @@ -113,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')
Expand Down Expand Up @@ -164,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')
Expand Down Expand Up @@ -201,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())
Expand All @@ -220,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'
Expand All @@ -236,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() == """
Expand All @@ -245,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
Expand All @@ -264,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


Expand Down Expand Up @@ -302,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')
Expand Down Expand Up @@ -357,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
Expand All @@ -387,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'
Expand All @@ -411,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
Expand All @@ -433,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())
Expand Down Expand Up @@ -505,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))
Expand All @@ -517,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']
Expand All @@ -534,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())
Expand Down Expand Up @@ -566,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
Expand Down Expand Up @@ -611,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!')]))
Expand Down Expand Up @@ -781,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)'
Expand Down Expand Up @@ -1274,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
Expand Down Expand Up @@ -1794,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):
"""
Expand Down
12 changes: 6 additions & 6 deletions loki/transformations/tests/test_data_offload.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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'
Expand Down
6 changes: 3 additions & 3 deletions loki/transformations/tests/test_inline.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
Expand Down
Loading

0 comments on commit 8837076

Please sign in to comment.