Skip to content

Commit

Permalink
Merge pull request #2825 from stfc/2271_ref_accesses
Browse files Browse the repository at this point in the history
(Towards #2271, closes #2278, closes #2849) Generalise reference_accesses() and use to tidy KernelModuleInlineTrans.
  • Loading branch information
sergisiso authored Jan 23, 2025
2 parents f534847 + e4c1ad3 commit 42a5e59
Show file tree
Hide file tree
Showing 51 changed files with 884 additions and 453 deletions.
3 changes: 3 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
13) PR #2862 for #2858. Change to PSyIR frontend and backend to ensure
existing parentheses in expressions are preserved.

14) PR #2825 for #2278 and #2849. Signatures AccessType now indentifies
INQUIRY and TYPE_INFO, for inquiry intrinsic accesses and precision symbols.

release 3.0.0 6th of December 2024

1) PR #2477 for #2463. Add support for Fortran Namelist statements.
Expand Down
46 changes: 20 additions & 26 deletions doc/developer_guide/dependency.rst
Original file line number Diff line number Diff line change
Expand Up @@ -263,16 +263,17 @@ DataAccess class i.e. the `_field_write_arguments()` and
Variable Accesses
=================

Especially in the NEMO API, it is not possible to rely on pre-defined
kernel information to determine dependencies between loops. So an additional,
somewhat lower-level API has been implemented that can be used to determine
variable accesses (READ, WRITE etc.), which is based on the PSyIR information.
The only exception to this is if a kernel is called, in which case the
metadata for the kernel declaration will be used to determine the variable
accesses for the call statement. The information about all variable usage
of a PSyIR node or a list of nodes can be gathered by creating an object of
type `psyclone.core.VariablesAccessInfo`.
This class uses a `Signature` object to keep track of the variables used.
When using PSyclone with generic Fortran code, it is not possible to
rely on pre-defined kernel information to determine dependencies
between loops. So an additional, somewhat lower-level API has been
implemented that can be used to determine variable accesses (READ,
WRITE etc.), which is based on the PSyIR information. The only
exception to this is if a kernel is called, in which case the metadata
for the kernel declaration will be used to determine the variable
accesses for the call statement. The information about all variable
usage of a PSyIR node or a list of nodes can be gathered by creating
an object of type `psyclone.core.VariablesAccessInfo`. This class
uses a `Signature` object to keep track of the variables used.

Signature
---------
Expand All @@ -289,6 +290,14 @@ a single component.
:members:
:special-members: __hash__, __eq__, __lt__

AccessType
----------

An individual access to a ``Signature`` is described by an instance of the
``AccessType`` enumeration:

.. autoclass:: psyclone.core.access_type.AccessType
:members:

VariablesAccessInfo
-------------------
Expand Down Expand Up @@ -319,22 +328,7 @@ instance is holding information about.
VariablesAccessInfo Options
+++++++++++++++++++++++++++

By default, `VariablesAccessInfo` will not report the first argument of
the PSyIR operators `lbound`, `ubound`, or `size` as read accesses,
since these functions do not actually access the content of the array,
they only query the size. If these accesses are required (e.g. in kernel
extraction this could be important if an array is only used in these
intrinsic - a driver would still need these arrays in order to query
the size), the optional `options` parameter of the `VariablesAccessInfo`
constructor can be used: add the key
`COLLECT-ARRAY-SHAPE-READS` and set it to true::

vai = VariablesAccessInfo(options={'COLLECT-ARRAY-SHAPE-READS': True})

In this case all arrays specified as first parameter to one of the
PSyIR operators above will be reported as read access.

Fortran also allows to rename a symbol locally when it is being imported,
Fortran allows an imported symbol to be renamed locally
(`use some_mod, only: renamed => original_name`). Depending on use case,
it might be useful to get the non-local, original name. By default,
`VariablesAccessInfo` will report the local name (i.e. the renamed name),
Expand Down
5 changes: 4 additions & 1 deletion examples/nemo/eg5/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,14 @@ kernels: extract_kernels.py
$(PSYCLONE) -l all -s ./extract_kernels.py -o psy.f90 ../code/tra_adv.F90

$(EXTRACT_DIR)/$(LIB_NAME):
make -C $(EXTRACT_DIR)
${MAKE} -C $(EXTRACT_DIR)

# Compilation uses the 'kernels' transformed code
psy.f90: kernels
psy.o: $(EXTRACT_DIR)/$(LIB_NAME)

%.o: %.f90
$(F90) $(F90FLAGS) -c $<

allclean: clean
${MAKE} -C ${EXTRACT_DIR} allclean
11 changes: 1 addition & 10 deletions examples/nemo/eg5/extract_kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,7 @@ def trans(psyir):
if not isinstance(kern, Loop):
continue
try:
# TODO #2080: once this is fixed, the option can be removed
# The example contains array expressions, e.g.:
# zwx(:,:,jpk) = 0.e0
# PSyclone represents this internally using Range with LBOUND
# and UBOUND intrinsics and currently this results in several
# occurrences of zws on the left hand side, which will trigger
# an exception in the dependency analysis. Therefore, disable
# the collection of read accesses for the shape of an array.
extract.apply(kern,
options={"COLLECT-ARRAY-SHAPE-READS": False})
extract.apply(kern)
except TransformationError as err:
# Typically that's caused by a kernel having a CodeBlock
# inside.
Expand Down
9 changes: 6 additions & 3 deletions examples/nemo/eg6/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ run: compile
./dummy

dummy: psy.o $(READ_ONLY_CHECK_DIR)/$(LIB_NAME)
$(F90) psy.o -o dummy $(READ_ONLY_CHECK_DIR)/$(LIB_NAME) $(LDFLAGS)
$(F90) $(F90FLAGS) psy.o -o dummy $(READ_ONLY_CHECK_DIR)/$(LIB_NAME) $(LDFLAGS)

transform: kernels

Expand All @@ -73,11 +73,14 @@ kernels: read_only_check.py
$(PSYCLONE) -l all -s ./read_only_check.py -o psy.f90 dummy.f90

$(READ_ONLY_CHECK_DIR)/$(LIB_NAME):
make -C $(READ_ONLY_CHECK_DIR)
$(MAKE) -C $(READ_ONLY_CHECK_DIR)

# Compilation uses the 'kernels' transformed code
psy.f90: dummy.f90 read_only_check.py
psy.f90: dummy.f90 read_only_check.py kernels
psy.o: $(READ_ONLY_CHECK_DIR)/$(LIB_NAME)

%.o: %.f90
$(F90) $(F90FLAGS) -c $<

allclean: clean
${MAKE} -C $(READ_ONLY_CHECK_DIR) allclean
2 changes: 1 addition & 1 deletion lib/read_only/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ read_only_base.o: psy_data_base.o
clean:
rm -f read_only_base.f90 psy_data_base.f90 *.o *.mod

allclean:
allclean: clean
$(MAKE) -C dl_esm_inf allclean
$(MAKE) -C lfric allclean
$(MAKE) -C generic allclean
62 changes: 45 additions & 17 deletions src/psyclone/core/access_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,54 +37,73 @@

'''This module implements the AccessType used throughout PSyclone.'''

from __future__ import print_function, absolute_import
from enum import Enum
from psyclone.configuration import Config


class AccessType(Enum):
'''A simple enum-class for the various valid access types.
'''

#: Data associated with the symbol is read.
READ = 1
#: Data associated with the symbols is written.
WRITE = 2
#: Data associated with the symbol is both read and written (e.g. is passed
#: to a routine with intent(inout)).
READWRITE = 3
#: Incremented from more than one cell column (see the LFRic API section
#: of the User Guide).
INC = 4
#: Read before incrementing. Requires that the outermost halo be clean (see
#: the LFRic API section of the User Guide).
READINC = 5
#: Is the output of a SUM reduction.
SUM = 6
# This is used internally to indicate unknown access type of
# a variable, e.g. when a variable is passed to a subroutine
# and the access type of this variable in the subroutine
# is unknown
#: This is used internally to indicate unknown access type of
#: a variable, e.g. when a variable is passed to a subroutine
#: and the access type of this variable in the subroutine
#: is unknown.
#: TODO #2863 - VariablesAccessInfo does not currently consider
#: UNKNOWN accesses and it should!
UNKNOWN = 7
#: A symbol representing a routine is called.
CALL = 8
#: The property/ies of a symbol is/are queried but the data it
#: represents is not accessed (e.g. 'var' in SIZE(var, dim=1)).
INQUIRY = 9
#: The symbol is used to access its type information (available at compile
#: time) - e.g. precision values such as 'wp' in 1.0_wp.
TYPE_INFO = 10

def __str__(self):
def __str__(self) -> str:
'''Convert to a string representation, returning just the
enum (e.g. 'WRITE')..
:return: API name for this string.
:rtype: str
enum (e.g. 'WRITE').
'''
# pylint complains without str() that the return type is not a str
return str(self.name)

def api_specific_name(self):
def api_specific_name(self) -> str:
'''This convenience function returns the name of the type in the
current API. E.g. in a lfric API, WRITE --> "gh_write"
current API. E.g. in the lfric API, WRITE --> "gh_write". If no
mapping is available then the generic name is returned.
:returns: The API specific name.
:rtype: str
'''
api_config = Config.get().api_conf()
rev_access_mapping = api_config.get_reverse_access_mapping()
return rev_access_mapping[self]
return rev_access_mapping.get(self, str(self).lower())

@staticmethod
def from_string(access_string):
def from_string(access_string: str):
'''Convert a string (e.g. "read") into the corresponding
AccessType enum value (AccessType.READ).
:param str access_string: Access type as string.
:param access_string: Access type as a string.
:returns: Corresponding AccessType enum.
:Raises: ValueError if access_string is not a valid access type.
:rtype: :py:class:`psyclone.core.access_type.AccessType`
:raises ValueError: if access_string is not a valid access type.
'''
for access in AccessType:
if access.name == access_string.upper():
Expand Down Expand Up @@ -128,6 +147,15 @@ def get_valid_reduction_names():
return [access.api_specific_name() for access in
AccessType.get_valid_reduction_modes()]

@staticmethod
def non_data_accesses():
'''
:returns: all access types that do not touch any data associated with
a symbol.
:rtype: list[:py:class:`psyclone.core.AccessType`]
'''
return [AccessType.CALL, AccessType.TYPE_INFO, AccessType.INQUIRY]


# ---------- Documentation utils -------------------------------------------- #
# The list of module members that we wish AutoAPI to generate
Expand Down
Loading

0 comments on commit 42a5e59

Please sign in to comment.