Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

introduce 'flatten_arrays()' (to overcome pointer hack) #199

Merged
merged 5 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions loki/backend/cgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,8 @@ def visit_Subroutine(self, o, **kwargs):
# Generate header with argument signature
aptr = []
for a in o.arguments:
# TODO: Oh dear, the pointer derivation is beyond hacky; clean up!
if isinstance(a, Array) > 0:
aptr += ['* restrict v_']
aptr += ['* restrict ']
elif isinstance(a.type.dtype, DerivedType):
aptr += ['*']
elif a.type.pointer:
Expand All @@ -176,18 +175,6 @@ def visit_Subroutine(self, o, **kwargs):
# ...and generate the spec without imports and argument declarations
body = [self.visit(o.spec, skip_imports=True, skip_argument_declarations=True, **kwargs)]

# Generate the array casts for pointer arguments
if any(isinstance(a, Array) for a in o.arguments):
body += [self.format_line('/* Array casts for pointer arguments */')]
for a in o.arguments:
if isinstance(a, Array):
dtype = self.visit(a.type, **kwargs)
# str(d).lower() is a bad hack to ensure caps-alignment
outer_dims = ''.join(f'[{self.visit(d, **kwargs).lower()}]'
for d in a.dimensions[1:])
body += [self.format_line(dtype, ' (*', a.name.lower(), ')', outer_dims, ' = (',
dtype, ' (*)', outer_dims, ') v_', a.name.lower(), ';')]

# Fill the body
body += [self.visit(o.body, **kwargs)]
body += [self.format_line('return 0;')]
Expand Down
7 changes: 4 additions & 3 deletions loki/transform/fortran_c_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from loki.transform.transformation import Transformation
from loki.transform.transform_array_indexing import (
shift_to_zero_indexing, invert_array_indices,
resolve_vector_notation, normalize_range_indexing
resolve_vector_notation, normalize_array_shape_and_access,
flatten_arrays
)
from loki.transform.transform_associates import resolve_associates
from loki.transform.transform_utilities import (
Expand All @@ -36,7 +37,6 @@
from loki.tools import as_tuple, flatten
from loki.types import BasicType, DerivedType, SymbolAttributes


__all__ = ['FortranCTransformation']


Expand Down Expand Up @@ -368,12 +368,13 @@ def generate_c_kernel(self, routine, **kwargs):

# Clean up Fortran vector notation
resolve_vector_notation(kernel)
normalize_range_indexing(kernel)
normalize_array_shape_and_access(kernel)

# Convert array indexing to C conventions
# TODO: Resolve reductions (eg. SUM(myvar(:)))
invert_array_indices(kernel)
shift_to_zero_indexing(kernel)
flatten_arrays(kernel, order='C', start_index=0)

# Inline all known parameters, since they can be used in declarations,
# and thus need to be known before we can fetch them via getters.
Expand Down
86 changes: 85 additions & 1 deletion loki/transform/transform_array_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
'shift_to_zero_indexing', 'invert_array_indices',
'resolve_vector_notation', 'normalize_range_indexing',
'promote_variables', 'promote_nonmatching_variables',
'promotion_dimensions_from_loop_nest', 'demote_variables'
'promotion_dimensions_from_loop_nest', 'demote_variables',
'flatten_arrays', 'normalize_array_shape_and_access'
]


Expand Down Expand Up @@ -496,3 +497,86 @@ def demote_variables(routine, variable_names, dimensions):
routine.spec = Transformer(decl_map).visit(routine.spec)

info(f'[Loki::Transform] Demoted variables in {routine.name}: {", ".join(variable_names)}')

def normalize_array_shape_and_access(routine):
"""
Shift all arrays to start counting at "1"
"""
def is_range_index(dim):
return isinstance(dim, sym.RangeIndex) and not dim.lower == 1

vmap = {}
for v in FindVariables(unique=False).visit(routine.body):
if isinstance(v, sym.Array):
new_dims = []
for i, d in enumerate(v.shape):
if isinstance(d, sym.RangeIndex):
if isinstance(v.dimensions[i], sym.RangeIndex):
start = simplify(v.dimensions[i].start - d.start + 1) if d.start is not None else None
stop = simplify(v.dimensions[i].stop - d.start + 1) if d.stop is not None else None
new_dims += [sym.RangeIndex((start, stop, d.step))]
else:
start = simplify(v.dimensions[i] - d.start + 1) if d.start is not None else None
new_dims += [start]
else:
new_dims += [v.dimensions[i]]
vmap[v] = v.clone(dimensions=as_tuple(new_dims))
routine.body = SubstituteExpressions(vmap).visit(routine.body)

vmap = {}
for v in routine.variables:
if isinstance(v, sym.Array):
new_dims = [sym.RangeIndex((1, simplify(d.upper - d.lower + 1)))
if is_range_index(d) else d for d in v.dimensions]
new_shape = [sym.RangeIndex((1, simplify(d.upper - d.lower + 1)))
if is_range_index(d) else d for d in v.shape]
new_type = v.type.clone(shape=as_tuple(new_shape))
vmap[v] = v.clone(dimensions=as_tuple(new_dims), type=new_type)
routine.variables = [vmap.get(v, v) for v in routine.variables]
normalize_range_indexing(routine)


def flatten_arrays(routine, order='F', start_index=1):
"""
Flatten arrays, converting multi-dimensional arrays to
one-dimensional arrays.

Parameters
----------
routine : :any:`Subroutine`
The subroutine in which the variables should be promoted.
order : str
Assume Fortran (F) vs. C memory/array order.
start_index : int
Assume array indexing starts with `start_index`.
"""
def new_dims(dim, shape):
if len(dim) > 1:
if isinstance(shape[-2], sym.RangeIndex):
raise TypeError(f'Resolve shapes being of type RangeIndex, e.g., "{shape[-2]}" before flattening!')
_dim = (sym.Sum((dim[-2], sym.Product((shape[-2], dim[-1] - start_index)))),)
new_dim = dim[:-2]
new_dim += _dim
return new_dims(new_dim, shape[:-1])
return dim

if order == 'C':
array_map = {
var: var.clone(dimensions=new_dims(var.dimensions[::-1], var.shape[::-1]))
for var in FindVariables().visit(routine.body)
if isinstance(var, sym.Array) and var.shape and len(var.shape)
}
elif order == 'F':
array_map = {
var: var.clone(dimensions=new_dims(var.dimensions, var.shape))
for var in FindVariables().visit(routine.body)
if isinstance(var, sym.Array) and var.shape and len(var.shape)
}
else:
raise ValueError(f'Unsupported array order "{order}"')

routine.body = SubstituteExpressions(array_map).visit(routine.body)

routine.variables = [v.clone(dimensions=as_tuple(sym.Product(v.shape)),
type=v.type.clone(shape=as_tuple(sym.Product(v.shape))))
if isinstance(v, sym.Array) else v for v in routine.variables]
Loading