Skip to content

(Towards #3367) def use chain update for assignments.#3399

Open
LonelyCat124 wants to merge 30 commits intomasterfrom
3367_def_use_chain_update
Open

(Towards #3367) def use chain update for assignments.#3399
LonelyCat124 wants to merge 30 commits intomasterfrom
3367_def_use_chain_update

Conversation

@LonelyCat124
Copy link
Copy Markdown
Collaborator

I now have this at a stage that it can handle multiple inputs and behave somewhat sensibly. I haven't yet finished, as it currently requires inputs to be members of the same parent, but I think we want them to simply share an ancestor statement.

There are a few remaining FIXMEs for things that I don't know what they were preventing previously and didn't cause any previous test failures, but presumably were things I thought mattered previously (though I did also just find some code that didn't matter so...). I need to do more testing for multi-inputs, but I think I might do that through Statement.next_accesses or some similar option.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.96%. Comparing base (f2023c8) to head (ac8fb18).

Additional details and impacted files
@@           Coverage Diff            @@
##           master    #3399    +/-   ##
========================================
  Coverage   99.96%   99.96%            
========================================
  Files         391      391            
  Lines       54583    54689   +106     
========================================
+ Hits        54564    54670   +106     
  Misses         19       19            

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@LonelyCat124
Copy link
Copy Markdown
Collaborator Author

I resolved the last of the FIXMEs. I need to do some tests with loops and things where I check I get the same results for Assignment.next_accesses/previous_accesses that I would calling it for all of the References individually.

@LonelyCat124
Copy link
Copy Markdown
Collaborator Author

Having an issue with this code:

subroutine test
    integer :: a, b
    logical :: x
    a = b
    if (x) then
        a = b + a
    else
        b = a * b
    end if
    a = 1
    b = 1
    end subroutine test

For the single reference case, it doesn't find b=1 for a forward check for the original b Reference, and I can't quite work out the issue. There seems to be something with the b = a * b accesses seemingly not belonging to a control flow node, so the DUCs are assuming it must always happens and ends the chain, which is incorrect. I'm trying to sort this out but its a bit confused (as far as I can work out it also doesn't happen for the a access which is super weird.

…t shows some easy cases for forward accesses
@LonelyCat124
Copy link
Copy Markdown
Collaborator Author

Fixed the previous case, but immediately created another non-equivalent case where I do

a = b
b = 2
...

as the def use chains output for b when the full statement is input goes past the b = 2 statement incorrectly.

@LonelyCat124
Copy link
Copy Markdown
Collaborator Author

I fixed the above case going forwards, so things are looking like they're getting there. I suspect there will be issues going backwards that I'll need to match as well.

@LonelyCat124
Copy link
Copy Markdown
Collaborator Author

NEMO ITs failing in the async runs. I need to manually look at whats happening - I think barriers are now being missed, which if there's not a simple solution to it might need a full DUC rewrite 😢

Copy link
Copy Markdown
Member

@arporter arporter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Aidan, that is extremely complex code! I've had a look through most of it except the tests so far. You'll see I'm a bit confused about the correct way of determining the "start point" and "end point". Apart from that, it's just minor things.

Comment thread src/psyclone/psyir/nodes/assignment.py Outdated
Comment thread src/psyclone/psyir/nodes/assignment.py Outdated
Comment thread src/psyclone/psyir/nodes/reference.py Outdated
Comment thread src/psyclone/psyir/nodes/reference.py Outdated
Comment thread src/psyclone/psyir/tools/definition_use_chains.py
Comment thread src/psyclone/psyir/tools/definition_use_chains.py Outdated
Comment thread src/psyclone/psyir/tools/definition_use_chains.py Outdated
self._start_point = self._reference_abs_pos
# Find the highest abs position, as all of these are
# contained in the same parent.
self._start_point = max(list(self._references_abs_pos.values()))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, we only start the search after the last of the provided references?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I felt like for a next_accesses on a statement such as b = a + a we wouldn't want to return the second reference to a. I guess in theory with how the chains are currently setup for an example like b = a + a + a you could provide a list with only b and the first and last a references in which case we'd miss the middle a, but I think that isn't a real case so I'm not worried.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, thanks. Worth saying this (the first sentence) in a comment.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

self._stop_point = self._reference_abs_pos
# Find the max abs position, as all of these are
# contained in the same parent.
self._stop_point = min(list(self._references_abs_pos.values()))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You use min but say "max" in the comment :-)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, as with my comments on the start_point, is min correct?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment - I think min is correct as discussed in the previous comment, but I could be persuaded either way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment as discussed on the previous comment.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

Comment thread src/psyclone/psyir/tools/definition_use_chains.py Outdated
@LonelyCat124
Copy link
Copy Markdown
Collaborator Author

@arporter Review comments addressed I think. I've tried to explain my reasoning for using max or min, but happy to change it if you disagree with what the result should be from the DUC.

@LonelyCat124
Copy link
Copy Markdown
Collaborator Author

I guess to also note this only improves the behaviour for Assignments, and the issue says statements, so I'm going to change this to towards 3367 and update the title and fix that later.

@LonelyCat124 LonelyCat124 changed the title (Closes #3367) def use chain update for statements. (Towards #3367) def use chain update for assignments. Apr 21, 2026
@LonelyCat124 LonelyCat124 requested a review from arporter April 23, 2026 09:03
Copy link
Copy Markdown
Member

@arporter arporter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks very much Aidan. I've made it all the way through now and it just comments and tidying left to do :-)
(ITs were all green bar a signal 11 on the LFRic tests which I've just restarted.)

)
control_flow_nodes.append(None)
index = len(chains)
control_flow_nodes.insert(index, None)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seem to be generally confused here. You get the length of chains and use that as the point at which to insert into control_flow_nodes - I don't think I'd fully understood that last time. Please add a comment to explain - this could also go some way to investigating whether (future) refactoring would help.

)
control_flow_nodes.append(None)
index = len(chains)
control_flow_nodes.insert(index, None)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not advocating for a re-write of anything at this stage - or at least, not in this PR. You understand the code a lot better than I do so if you think it would simplify things then that would be great, otherwise, don't worry.

self._start_point = self._reference_abs_pos
# Find the highest abs position, as all of these are
# contained in the same parent.
self._start_point = max(list(self._references_abs_pos.values()))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, thanks. Worth saying this (the first sentence) in a comment.

self._stop_point = self._reference_abs_pos
# Find the max abs position, as all of these are
# contained in the same parent.
self._stop_point = min(list(self._references_abs_pos.values()))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment as discussed on the previous comment.

sig = a_3.get_signature_and_indices()[0]
duc = DefinitionUseChain(
a_3, control_flow_region=[routine]
[a_3], control_flow_region=[routine]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to put a_3 in a list now.


psyir = fortran_reader.psyir_from_source(code)
references = psyir.walk(Reference)
next_accesses = references[4].next_accesses()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some comments to explain which bits of the code are being examined and found.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

'''Test the DUCs give the same results for multiple inputs as each of
the inputs individually.
'''

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a small thing but you could reduce duplication in the fixture by doing:

code = f"subroutine test\n{code}\nend subroutine test"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

'''Test the DUCs give the same results for multiple inputs as each
of the inputs individually.
'''
psyir = fortran_reader.psyir_from_source(code)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here.

assign = psyir.walk(Assignment)[-1]

all_refs = assign.walk(Reference)
lhs_ref = all_refs[0] # First Reference is lhs a
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth sanity-checking this in case of future changes, e.g. assert lhs_ref.symbol.name == "a" and same for rhs_ref.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

assign = psyir.walk(Assignment)[0]

all_refs = assign.walk(Reference)
lhs_ref = all_refs[0] # First Reference is lhs a
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls add sanity checks that the refs are the ones you expect, e.g. assert lhs_ref.symbol.name == "a"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

@LonelyCat124
Copy link
Copy Markdown
Collaborator Author

@arporter Ready for another look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants