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

2704 backward accesses #2814

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
10) PR #2810. Adds caching of the fparser2 parse tree to FileInfo. Is
disabled by default.

11) PR #2814 for #2704. Adds backward accesses capabilities to the
DefinitionUseChain tool.

release 3.0.0 6th of December 2024

1) PR #2477 for #2463. Add support for Fortran Namelist statements.
Expand Down
6 changes: 3 additions & 3 deletions doc/developer_guide/dependency.rst
Original file line number Diff line number Diff line change
Expand Up @@ -666,12 +666,12 @@ can be parallelised:
DefinitionUseChain
==================
PSyclone also provides a DefinitionUseChain class, which can search for forward
dependencies (backward NYI) for a given Reference inside a region of code. This
and backward dependencies for a given Reference inside a region of code. This
implementation differs from the DependencyTools as it is control-flow aware, so
can find many dependencies for a single Reference in a given Routine or scope.

This is primarily used to implement the `References.next_accesses` function, but can be
used directly as follows:
This is primarily used to implement the `Reference.next_accesses` and
`Reference.previous_accessess` functions, but can be used directly as follows:

.. code::

Expand Down
34 changes: 9 additions & 25 deletions src/psyclone/psyir/nodes/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
''' This module contains the implementation of the Reference node.'''


from psyclone.core import AccessType, Signature, VariablesAccessInfo
from psyclone.core import AccessType, Signature
# We cannot import from 'nodes' directly due to circular import
from psyclone.psyir.nodes.datanode import DataNode
from psyclone.psyir.symbols import Symbol
Expand Down Expand Up @@ -233,34 +233,18 @@ def datatype(self):
return UnresolvedType()
return self.symbol.datatype

def previous_access(self):
def previous_accesses(self):
'''
:returns: the previous reference to the same symbol.
:rtype: Optional[:py:class:`psyclone.psyir.nodes.Node`]
:returns: the nodes accessing the same symbol directly before this
reference. It can be multiple nodes if the control flow
diverges and there are multiple possible accesses.
:rtype: List[:py:class:`psyclone.psyir.nodes.Node`]
'''
# Avoid circular import
# pylint: disable=import-outside-toplevel
from psyclone.psyir.nodes.routine import Routine
# The scope is as far as the Routine that contains this
# Reference.
routine = self.ancestor(Routine)
# Handle the case when this is a subtree without an ancestor
# Routine
if routine is None:
routine = self.root
var_access = VariablesAccessInfo(nodes=routine)
signature, _ = self.get_signature_and_indices()
all_accesses = var_access[signature].all_accesses
index = -1
# Find my position in the VariablesAccesInfo
for i, access in enumerate(all_accesses):
if access.node is self:
index = i
break

if index > 0:
return all_accesses[index-1].node
return None
from psyclone.psyir.tools import DefinitionUseChain
chain = DefinitionUseChain(self)
return chain.find_backward_accesses()

def next_accesses(self):
'''
Expand Down
442 changes: 422 additions & 20 deletions src/psyclone/psyir/tools/definition_use_chains.py

Large diffs are not rendered by default.

93 changes: 61 additions & 32 deletions src/psyclone/tests/psyir/nodes/reference_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,15 +247,31 @@ def test_reference_next_accesses(fortran_reader):
end subroutine'''
psyir = fortran_reader.psyir_from_source(code)
routine = psyir.children[0]
a = routine.children[0].lhs
a_before_loop = routine.children[0].lhs
loop = routine.children[1]
b = loop.loop_body.children[0].lhs
a_2 = loop.loop_body.children[0].rhs
assert len(a.next_accesses()) == 1
assert a.next_accesses()[0] is loop
assert len(a_before_loop.next_accesses()) == 1
assert a_before_loop.next_accesses()[0] is loop
assert len(b.next_accesses()) == 1
assert b.next_accesses()[0] == b

# Check that a loop accessing a variable before
# the reference doesn't result in a false positive.
code = '''subroutine my_sub()
integer :: a
integer :: b
do a = 0, 10
b = a
end do
a = 1
end subroutine'''
psyir = fortran_reader.psyir_from_source(code)
routine = psyir.children[0]
a_after_loop = routine.children[1].lhs
loop = routine.children[0]
b = loop.loop_body.children[0].lhs
assert len(a_after_loop.next_accesses()) == 0

# Check the function for basic structures
code = '''subroutine my_sub()
type :: x
Expand Down Expand Up @@ -359,8 +375,8 @@ def test_reference_next_accesses_with_codeblock(fortran_reader):
assert a.next_accesses()[0] is codeblock


def test_reference_previous_access(fortran_reader):
'''Test the previous_access function for a Reference'''
def test_reference_previous_accesses(fortran_reader):
'''Test the previous_accesses function for a Reference'''
code = '''subroutine my_sub()
integer :: a
integer :: b
Expand All @@ -374,10 +390,10 @@ def test_reference_previous_access(fortran_reader):
b = routine.children[1].lhs
a_2 = routine.children[2].lhs
b_2 = routine.children[2].rhs
assert a.previous_access() is None
assert b.previous_access() is None
assert a_2.previous_access() is a
assert b_2.previous_access() is b
assert len(a.previous_accesses()) == 0
assert len(b.previous_accesses()) == 0
assert a_2.previous_accesses()[0] is a
assert b_2.previous_accesses()[0] is b

code = '''subroutine my_sub()
integer :: a
Expand All @@ -393,9 +409,11 @@ def test_reference_previous_access(fortran_reader):
loop = routine.children[1]
b_a = loop.loop_body.children[0].lhs
a_2 = loop.loop_body.children[0].rhs
assert a.previous_access() is None
assert b_a.previous_access() is None
assert a_2.previous_access() is loop
assert len(a.previous_accesses()) == 0
assert len(b_a.previous_accesses()) == 1
assert b_a.previous_accesses()[0] is b_a
assert len(a_2.previous_accesses()) == 1
assert a_2.previous_accesses()[0] is loop

# Check the function for basic structures
code = '''subroutine my_sub()
Expand All @@ -415,10 +433,12 @@ def test_reference_previous_access(fortran_reader):
b = routine.children[1].lhs
a_2 = routine.children[2].lhs
b_2 = routine.children[3].lhs
assert a.previous_access() is None
assert b.previous_access() is None
assert a_2.previous_access() is a
assert b_2.previous_access() is b
assert len(a.previous_accesses()) == 0
assert len(b.previous_accesses()) == 0
assert len(a_2.previous_accesses()) == 1
assert len(b_2.previous_accesses()) == 1
assert a_2.previous_accesses()[0] is a
assert b_2.previous_accesses()[0] is b

# Check the function for array access
code = '''subroutine my_sub()
Expand All @@ -430,8 +450,9 @@ def test_reference_previous_access(fortran_reader):
routine = psyir.children[0]
a = routine.children[0].lhs
a_2 = routine.children[1].lhs
assert a.previous_access() is None
assert a_2.previous_access() is a
assert len(a.previous_accesses()) == 0
assert len(a_2.previous_accesses()) == 1
assert a_2.previous_accesses()[0] is a

# Check if statements
code = '''subroutine my_sub()
Expand All @@ -448,9 +469,12 @@ def test_reference_previous_access(fortran_reader):
a = routine.children[0].lhs
a_2 = routine.children[1].if_body.children[0].lhs
a_3 = routine.children[2].lhs
assert a.previous_access() is None
assert a_2.previous_access() is a
assert a_3.previous_access() is a_2
assert len(a.previous_accesses()) == 0
assert len(a_2.previous_accesses()) == 1
assert a_2.previous_accesses()[0] is a
assert len(a_3.previous_accesses()) == 2
assert a_3.previous_accesses()[0] is a_2
assert a_3.previous_accesses()[1] is a

# Check else block behaviour
code = '''subroutine my_sub()
Expand All @@ -470,10 +494,15 @@ def test_reference_previous_access(fortran_reader):
a_2 = routine.children[1].if_body.children[0].lhs
a_3 = routine.children[1].else_body.children[0].lhs
a_4 = routine.children[2].lhs
assert a.previous_access() is None
assert a_2.previous_access() is a
assert a_3.previous_access() is a_2
assert a_4.previous_access() is a_3
assert len(a.previous_accesses()) == 0
assert len(a_2.previous_accesses()) == 1
assert a_2.previous_accesses()[0] is a
assert len(a_2.previous_accesses()) == 1
assert a_3.previous_accesses()[0] is a
assert len(a_4.previous_accesses()) == 3
assert a_4.previous_accesses()[0] is a_3
assert a_4.previous_accesses()[1] is a_2
assert a_4.previous_accesses()[2] is a


def test_reference_accesses_initialisation_statement(fortran_reader):
Expand All @@ -492,20 +521,20 @@ def test_reference_accesses_initialisation_statement(fortran_reader):
psyir = fortran_reader.psyir_from_source(code)
routine = psyir.children[0].children[0]
a = routine.children[0].lhs
assert a.previous_access() is None
assert len(a.previous_accesses()) == 0

sym_tab = routine.symbol_table
symbols = sym_tab.get_symbols()
b_sym = symbols['b']
refs = b_sym.initial_value.walk(Reference)
assert refs[0].next_accesses()[0] == refs[1]
assert refs[1].previous_access() == refs[0]
assert refs[0].previous_access() is None
assert refs[1].previous_accesses()[0] == refs[0]
assert len(refs[0].previous_accesses()) == 0
assert len(refs[1].next_accesses()) == 0


def test_reference_previous_access_with_codeblock(fortran_reader):
''' Test when te previous_access is a Codeblock. '''
def test_reference_previous_accesses_with_codeblock(fortran_reader):
''' Test when te previous_accesses is a Codeblock. '''
code = '''subroutine my_sub()
character, dimension(100) :: a
write(a, "A") "mytest"
Expand All @@ -516,7 +545,7 @@ def test_reference_previous_access_with_codeblock(fortran_reader):
routine = psyir.children[0]
a = routine.children[1].lhs
codeblock = routine.children[1]
if a.previous_access() is not codeblock:
if a.previous_accesses() is not codeblock:
pytest.xfail("#2271 Codeblocks don't currently support "
"reference_accesses")

Expand Down
Loading
Loading