Skip to content
Merged
47 changes: 1 addition & 46 deletions doc/developer_guide/dependency.rst
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ AccessInfo
The class `SingleVariableAccessInfo` uses a list of
`psyclone.core.AccessInfo` instances to store all
accesses to a single variable. A new instance of `AccessInfo`
is appended to the list whenever `add_access_with_location()`
is appended to the list whenever `add_access()`
is called.

.. autoclass:: psyclone.core.AccessInfo
Expand Down Expand Up @@ -446,51 +446,6 @@ wrapped in an outer loop over all accesses.

Index 'i' is used.

Access Location
---------------

Variable accesses are stored in the order in which they happen. For example,
an assignment `a=a+1` will store two access for the variable `a`, the
first one being a READ access, followed by a WRITE access, since this is the
order in which the accesses are executed.
Additionally, the function `reference_accesses()` keeps track of the location
at which the accesses happen. A location is an integer number, starting with 0,
which is increased for each new statement. This makes it possible to
compare accesses to variables: if two accesses have the same location value,
it means the accesses happen in the same statement, for example `a=a+1`:
the READ and WRITE access to `a` will have the same location number. If on the
other hand the accesses happen in two separate statements, e.g. `a=b+1; c=a+1`
then the first access to `a` (and the access to `b`) will have a smaller
location number than the second access to `a` (and the access to `c`).
If two statements have consecutive locations, this does not necessarily mean
that the statements are executed one after another. For example in if-statements
the statements in the if-body are counted first, then the statements in the
else-body. It is the responsibility of the user to handle these cases - for
example by creating separate `VariablesAccessMap` for statements in the if-body
and for the else-body.

.. note:: When using different instances for an if- and else-body, the first
statement of the if-body will
have the same location number as the first statement of the else-body. So
you can only compare location numbers from the same `VariablesAccessMap`
instance. If you merge two instances together, the locations of the merged-in
instance will be appropriately increased to follow the locations of the
instance to which it is merged.


The location number is not exactly a line number - several statements can be
on one line, which will get different location numbers. And certain lines
will not have a location number (e.g. comment lines).

As stated above, one instance of `VariablesAccessMap` can be extended by adding
additional variable information. It is the responsibility of the user to make
sure the accesses are added in the right order - the `VariablesAccessMap` object
will always assume accesses happen at the current location, and a call to
`next_location()` is required (internally) to increase the location number.

.. note:: It is not possible to add access information about an earlier
Comment thread
arporter marked this conversation as resolved.
usage to an existing `VariablesAccessMap` object.


Access Examples
---------------
Expand Down
153 changes: 23 additions & 130 deletions src/psyclone/core/single_variable_access_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,38 +40,31 @@
'''This module provides management of variable access information.'''


from __future__ import annotations
from typing import TYPE_CHECKING, Optional

from psyclone.core.access_type import AccessType
from psyclone.core.component_indices import ComponentIndices
from psyclone.errors import InternalError
if TYPE_CHECKING: # pragma: no cover
from psyclone.psyir.nodes import Node


class AccessInfo():
'''This class stores information about a single access
pattern of one variable (e.g. variable is read at a certain location).
A location is a number which can be used to compare different accesses
(i.e. if one access happens before another). Each consecutive
location will have an increasing location number, but read and write
accesses in the same statement will have the same location number.
If the variable accessed is an array, this class will also store
the indices used in the access.
Note that the name of the variable is not stored in this class.
It is a helper class used in the `SingleVariableAccessInfo` class,
which stores all `AccessInfo` objects for a variable, and it stores
the name of the variable.
''' This class stores information about an access to a variable (the node
where it happens and the type of access, and the index accessed if
available).

:param access: the access type.
:type access_type: :py:class:`psyclone.core.access_type.AccessType`
:param int location: a number used in ordering the accesses.
:param node: Node in PSyIR in which the access happens.
:type node: :py:class:`psyclone.psyir.nodes.Node`
:param component_indices: indices used in the access, defaults to None.
:type component_indices: None, [], a list or a list of lists of \
:py:class:`psyclone.psyir.nodes.Node` objects, or an object of type \
:py:class:`psyclone.core.component_indices.ComponentIndices`

'''
def __init__(self, access_type, location, node, component_indices=None):
self._location = location
def __init__(
self, access_type: AccessType, node: 'Node',
component_indices: Optional[list[list['Node']] |
ComponentIndices] = None
):
self._access_type = access_type
self._node = node
if not isinstance(component_indices, ComponentIndices):
Expand All @@ -80,9 +73,7 @@ def __init__(self, access_type, location, node, component_indices=None):
self.component_indices = component_indices

def __str__(self):
'''Returns a string representation showing the access mode
and location, e.g.: WRITE(5).'''
return f"{self._access_type}({self._location})"
return f"{self._access_type}"

def change_read_to_write(self):
'''This changes the access mode from READ to WRITE.
Expand Down Expand Up @@ -114,16 +105,14 @@ def component_indices(self):
return self._component_indices

@component_indices.setter
def component_indices(self, component_indices):
def component_indices(self, component_indices: ComponentIndices):
'''Sets the indices for this AccessInfo instance. The component_indices
contains a list of indices for each component of the signature,
e.g. for `a(i)%b(j,k)%c` the component_indices will be
`[ [i], [j, k], [] ]` (with each element being the PSyIR of the
index expression).

:param component_indices: indices used in the access.
:type component_indices: \
:py:class:`psyclone.core.component_indices.ComponentIndices`

:raises InternalError: if component_indices is not an instance \
of :py:class:`psyclone.core.component_indices.ComponentIndices`.
Expand Down Expand Up @@ -160,14 +149,6 @@ def is_data_access(self) -> bool:
'''
return self._access_type not in AccessType.non_data_accesses()

@property
def location(self):
''':returns: the location information for this access.\
Please see the Developers' Guide for more information.
:rtype: int
'''
return self._location

@property
def node(self):
''':returns: the PSyIR node at which this access happens.
Expand Down Expand Up @@ -205,21 +186,15 @@ class SingleVariableAccessInfo():
def __init__(self, signature):
self._signature = signature
# This is the list of AccessInfo instances for this variable.

self._accesses = []

def __str__(self):
'''Returns a string representation of this object with the format:
var_name:WRITE(2),WRITE(3),READ(5) where the numbers indicate
the 'location' of the corresponding access. The location is an
integer number that enumerates each statement in a program unit,
and can be used to compare if an access is earlier, later or in
the same statement as another access.

var_name:[WRITE,WRITE,READ]
'''
all_accesses = ",".join([str(access) for access in self._accesses])

return f"{self._signature}:{all_accesses}"
return f"{self._signature}:[{all_accesses}]"

def __repr__(self):
return ",".join([str(access) for access in self._accesses])
Expand Down Expand Up @@ -337,24 +312,19 @@ def all_write_accesses(self):
return [access for access in self._accesses
if access.access_type in AccessType.all_write_accesses()]

def add_access_with_location(self, access_type, location, node,
component_indices):
def add_access(
self, access_type: AccessType, node: 'Node',
component_indices: Optional[list[list['Node']] |
ComponentIndices] = None
):
'''Adds access information to this variable.

:param access_type: the type of access (READ, WRITE, ....)
:type access_type: \
:py:class:`psyclone.core.access_type.AccessType`
:param location: location information
:type location: int
:param node: Node in PSyIR in which the access happens.
:type node: :py:class:`psyclone.psyir.nodes.Node`
:param component_indices: indices used for each component of the \
access.
:type component_indices: \
:py:class:`psyclone.core.component_indices.ComponentIndices`
'''
self._accesses.append(AccessInfo(access_type, location, node,
component_indices))
self._accesses.append(AccessInfo(access_type, node, component_indices))

def change_read_to_write(self):
'''This function is only used when analysing an assignment statement.
Expand Down Expand Up @@ -425,83 +395,6 @@ def is_array(self, index_variable=None):
# The index variable is not used in any index in any access:
return False

def is_written_before(self, reference):
'''Returns True if this variable is written before the specified
reference, and False if not.

:param reference: the reference at which to stop for access checks.
:type reference: :py:class:`psyclone.psyir.nodes.Reference`

:returns: True if this variable is written before the specified \
reference, and False if not.
:rtype: bool

:raises ValueError: if the specified reference is not in the list of \
all accesses.

'''
result = False

for access in self._accesses:
if access.node is reference:
return result
if access.access_type == AccessType.WRITE:
result = True
raise ValueError(f"Reference not found in 'is_written_before' for "
f"variable '{self.var_name}'.")

def is_read_before(self, reference):
'''Returns True if this variable is read before the specified
reference, and False if not.

:param reference: the reference at which to stop for access checks.
:type reference: :py:class:`psyclone.psyir.nodes.Reference`

:returns: True if this variable is read before the specified \
reference, and False if not.
:rtype: bool

:raises ValueError: if the specified reference is not in the list of \
all accesses.

'''
result = False

for access in self._accesses:
if access.node is reference:
return result
if access.access_type == AccessType.READ:
result = True
raise ValueError(f"Reference not found in 'is_read_before' for "
f"variable '{self.var_name}'.")

def is_accessed_before(self, reference):
'''Returns True if this variable is accessed before the specified
reference, and False if not. This is equivalent to testing that
'reference' is the very first access, but this function will also
verify that 'reference' is indeed in the list of accesses.

:param reference: the reference at which to stop for access checks.
:type reference: :py:class:`psyclone.psyir.nodes.Reference`

:returns: True if this variable is read before the specified \
reference, and False if not.
:rtype: bool

:raises ValueError: if the specified reference is not in the list of \
all accesses.

'''

result = False

for access in self._accesses:
if access.node is reference:
return result
result = True
raise ValueError(f"Reference not found in 'is_accessed_before' for "
f"variable '{self.var_name}'.")


# ---------- Documentation utils -------------------------------------------- #
# The list of module members that we wish AutoAPI to generate
Expand Down
47 changes: 7 additions & 40 deletions src/psyclone/core/variables_access_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,10 @@

class VariablesAccessMap(dict):
''' This dictionary stores `SingleVariableAccessInfo` instances indexed by
their signature. It also maintains 'location' information, which is an
integer number that is increased for each new statement. It can be used to
easily determine if one access is syntactically before another.
their signature.

'''

def __init__(self):
super().__init__()
# Stores the current location information
self._location = 0

def __str__(self):
'''Gives a shortened visual representation of all variables
and their access mode. The output is one of: READ, WRITE, READ+WRITE,
Expand Down Expand Up @@ -97,20 +90,6 @@ def __str__(self):
output_list.append(f"{signature}: {mode}")
return ", ".join(output_list)

@property
def location(self):
'''Returns the current location of this instance, which is
the location at which the next accesses will be stored.
See the Developers' Guide for more information.

:returns: the current location of this object.
:rtype: int'''
return self._location

def next_location(self):
'''Increases the location number.'''
self._location = self._location + 1

def add_access(self, signature, access_type, node, component_indices=None):
'''Adds access information for the variable with the given signature.
If the `component_indices` parameter is not an instance of
Expand Down Expand Up @@ -172,13 +151,10 @@ def add_access(self, signature, access_type, node, component_indices=None):
f"requires {len(signature)} elements.")

if signature in self:
self[signature].add_access_with_location(access_type,
self._location, node,
component_indices)
self[signature].add_access(access_type, node, component_indices)
else:
var_info = SingleVariableAccessInfo(signature)
var_info.add_access_with_location(access_type, self._location,
node, component_indices)
var_info.add_access(access_type, node, component_indices)
self[signature] = var_info

@property
Expand Down Expand Up @@ -206,9 +182,7 @@ def all_data_accesses(self) -> List[Signature]:
def update(self, other_access_info):
''' Updates this dictionary with the entries in the provided
VariablesAccessMap. If there are repeated signatures, the provided
values are appeneded to the existing sequence of accesses. The
'location' property of the provided accesses (values in the dictionary)
will be updated to be after the existing entries.
values are appended to the existing sequence of accesses.

:param other_access_info: the other VariablesAccessMap instance.
:type other_access_info: :py:class:`psyclone.core.VariablesAccessMap`
Expand All @@ -217,22 +191,15 @@ def update(self, other_access_info):
for signature in other_access_info.all_signatures:
var_info = other_access_info[signature]
for access_info in var_info.all_accesses:
new_location = access_info.location + self._location
if signature in self:
var_info = self[signature]
else:
var_info = SingleVariableAccessInfo(signature)
self[signature] = var_info

var_info.add_access_with_location(access_info.access_type,
new_location,
access_info.node,
access_info.
component_indices)
# Increase the current location of this instance by the amount of
# locations just merged in
# pylint: disable=protected-access
self._location = self._location + other_access_info._location
var_info.add_access(access_info.access_type,
access_info.node,
access_info.component_indices)

def is_called(self, signature: Signature) -> bool:
'''
Expand Down
3 changes: 0 additions & 3 deletions src/psyclone/domain/lfric/lfric_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,6 @@ def reference_accesses(self) -> VariablesAccessMap:
var_accesses.add_access(Signature(name), arg.access, self)
# Now merge the write access to the end of all other accesses:
var_accesses.update(written)
# Forward location pointer to next index, since this built-in kernel
# finishes a statement
var_accesses.next_location()
return var_accesses

def load(self, call, parent=None):
Expand Down
Loading
Loading