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

Array shadowing bugfix for inliner #202

Merged
merged 6 commits into from
Dec 21, 2023

Conversation

skarppinen
Copy link
Contributor

Hello.

This PR fixes a bug in inline_subroutine_calls.
Consider the following example (here demonstrating with inline_member_procedures, which eventually calls the above):

from loki import Sourcefile
from loki.transform import (
    inline_member_procedures
)
fcode = """
    subroutine outer(klon)
        integer :: jg, jlon
        integer :: arr(3, 3)

        jg = 70000
        call inner2()

        contains

        subroutine inner2()
            integer :: jlon, jg 
            integer :: arr(3, 3)
            do jg=1,3
                do jlon=1,3
                   arr(jlon, jg) = 11 
                end do
            end do
        end subroutine inner2

    end subroutine outer
"""
src = Sourcefile.from_source(fcode)
routine = src.routines[0]
inline_member_procedures(routine)
print(routine.to_fortran())

On the main branch, this prints:

SUBROUTINE outer (klon)
  INTEGER :: jg, jlon
  INTEGER :: arr(3, 3)
  INTEGER :: inner2_jlon, inner2_jg
  INTEGER :: inner2_arr(3, 3)

  jg = 70000
  ! [Loki] inlined child subroutine: inner2
  ! =========================================
  DO inner2_jg=1,3
    DO inner2_jlon=1,3
      inner2_arr(jlon, jg) = 11 ! Wrong 'jlon' and 'jg'
    END DO
  END DO
  ! =========================================

  CONTAINS

END SUBROUTINE outer

Inside the double loop, note the wrong jlon and jg.
This occurs because the inliner renames shadowing variables only by "name", i.e arr(jlon, jg) is transformed only to inner_arr(jlon, jg) even though jlon and jg should also be renamed as they are shadowed as well.
This occurs here (var_map only changes name):

var_map = {}
duplicate_names = {dl.name.lower() for dl in duplicates}
for v in FindVariables(unique=False).visit(callee.body):
if v.name.lower() in duplicate_names:
var_map[v] = v.clone(name=f'{callee.name}_{v.name}')
callee.body = SubstituteExpressions(var_map).visit(callee.body)

This PR fixes the above issue by first building a transformation map for renaming nonarray variables, and then a transformation map for renaming array variables. In the construction of the latter, the dimensions attribute is transformed using the transformation map for nonarray variables. There may be a more elegant way to achieve this.

There is also a new test to check that this remains fixed.

Copy link

codecov bot commented Dec 20, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (556db2e) 92.26% compared to head (1796216) 92.26%.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #202   +/-   ##
=======================================
  Coverage   92.26%   92.26%           
=======================================
  Files          96       96           
  Lines       17092    17092           
=======================================
  Hits        15770    15770           
  Misses       1322     1322           
Flag Coverage Δ
lint_rules 96.22% <ø> (ø)
loki 92.24% <100.00%> (ø)
transformations 91.48% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@reuterbal
Copy link
Collaborator

Thank you very much for finding this and supplying test and fix. However, I think all that's missing here is a call to recursive_expression_map_update before applying the SubstituteExpressions. Would you mind adding just that at the place you have flagged, and seeing whether your test still passes?

… added import for it, remove local import found in same file to avoid duplicate imports
@skarppinen
Copy link
Contributor Author

Thanks @reuterbal, that's a lot simpler and works!

Note that there was an additional local import for recursive_expression_map_update in another function. I couldn't think of why it was introduced as a local import rather than global. When I removed it, the tests seemed to still pass, and everything seemed to be fine, so I removed the local import and just added a global import for the function to avoid duplicate imports.

Copy link
Collaborator

@reuterbal reuterbal left a comment

Choose a reason for hiding this comment

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

Fantastic, many thanks!

I don't remember what the history of the local import was, but the pylint annotations next to it suggest this may have been to break a cyclic import in the past. However, this does not seem to be a problem any more. Thanks for changing this!

@reuterbal reuterbal added the ready for merge This PR has been approved and is ready to be merged label Dec 21, 2023
Copy link
Collaborator

@mlange05 mlange05 left a comment

Choose a reason for hiding this comment

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

Many thanks for catching this @skarppinen and apologies, I must have dropped this in the recent refactoring. GTG from me. :shipit:

@mlange05 mlange05 merged commit 5c0f821 into ecmwf-ifs:main Dec 21, 2023
8 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready for merge This PR has been approved and is ready to be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants