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

Global var hoisting #226

Merged
merged 10 commits into from
Feb 9, 2024
Merged

Global var hoisting #226

merged 10 commits into from
Feb 9, 2024

Conversation

MichaelSt98
Copy link
Collaborator

Global/Imported variable hoisting.

Convert e.g.,

module moduleB
   real :: var2
   real :: var3
end module moduleB

module moduleC
   real :: var4
   real :: var5
end module moduleC

subroutine driver()
implicit none

call kernel()

end subroutine driver

subroutine kernel()
use moduleB, only: var2,var3
use moduleC, only: var4,var5
implicit none

var4 = var2
var5 = var3

end subroutine kernel

to:

module moduleB
   real :: var2
   real :: var3
end module moduleB

module moduleC
   real :: var4
   real :: var5
end module moduleC

subroutine driver()
use moduleB, only: var2,var3
use moduleC, only: var4,var5
implicit none

call kernel(var2, var3, var4, var5)

end subroutine driver

subroutine kernel(var2, var3, var4, var5)
implicit none
real, intent(in) :: var2
real, intent(in) :: var3
real, intent(inout) :: var4
real, intent(inout) :: var5

var4 = var2
var5 = var3

end subroutine kernel

Tested locally with CLOUDSC Loki C transpilation. However, this requires PR #208.

@MichaelSt98 MichaelSt98 marked this pull request as ready for review February 2, 2024 12:21
Copy link

github-actions bot commented Feb 2, 2024

Documentation for this branch can be viewed at https://sites.ecmwf.int/docs/loki/226/index.html

Copy link

codecov bot commented Feb 2, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (73c6635) 92.31% compared to head (a840d1d) 92.36%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #226      +/-   ##
==========================================
+ Coverage   92.31%   92.36%   +0.04%     
==========================================
  Files          95       95              
  Lines       17368    17478     +110     
==========================================
+ Hits        16034    16144     +110     
  Misses       1334     1334              
Flag Coverage Δ
lint_rules 96.22% <ø> (ø)
loki 92.31% <ø> (ø)
transformations 91.93% <100.00%> (+0.46%) ⬆️

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.

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.

Thanks, this looks very good to me!

I've left a few suggestions for stylistic improvements and flagged a potential issue when an import shadows an import in a parent scope. Arguably, this is an almost academic edge case and bad practice anyway, but the fix is easy.

@@ -409,22 +412,36 @@ def test_global_variable_analysis(frontend, key, config, global_variable_analysi
'global_var_analysis_kernel_mod#kernel_a': {
'defines_symbols': set(),
'uses_symbols': {
('iarr(1:3)', 'global_var_analysis_header_mod'), ('rarr(1:5, 1:3)', 'global_var_analysis_header_mod')
Copy link
Collaborator

Choose a reason for hiding this comment

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

I find the in-line special casing around OMNI hard to read. Especially, because it's mixed with a different way of distinguishing between OMNI and the others already. Could we maybe pull this out and add a corresponding comment that explains this?

A possible pattern would be this:

if frontend == OMNI:
    # OMNI replaces all parameters by their values. For other frontends we get the
    # parameters as additional analysis data entries
    nfld_dim = '1:3'
    nval_dim = '1:3'
    nfld_data = set()
    nval_data = set()
else:
    nfld_dim = 'nfld'
    nval_dim = 'nval'
    nfld_data = {('nfld', 'global_var_analysis_header_mod')}
    nval_data = {('nval', 'global_var_analysis_header_mod')}

expected_trafo_data = {
    'global_var_analysis_header_mod#nval': {
        ...
        'offload': nval_data,
    }
    ...
    'global_var_analysis_kernel_mod#kernel_a': {
        'defines_symbols': set(),
        'uses_symbols': nval_data | nfld_data | {
            (f'iarr({nfld_dim})', 'global_var_analysis_header_mod'),
            (f'rarr({nval_dim}, {nfld_dim})', 'global_var_analysis_header_mod'),
        },
    ...

transformations/tests/test_data_offload.py Show resolved Hide resolved
transformations/transformations/data_offload.py Outdated Show resolved Hide resolved
Comment on lines 748 to 753
all_defines_symbols = set()
all_uses_symbols = set()
for _, value in defines_symbols.items():
all_defines_symbols |= value
for _, value in uses_symbols.items():
all_uses_symbols |= value
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't this achieve the same?

Suggested change
all_defines_symbols = set()
all_uses_symbols = set()
for _, value in defines_symbols.items():
all_defines_symbols |= value
for _, value in uses_symbols.items():
all_uses_symbols |= value
all_defines_symbols = set.union(*defines_symbols.values(), set()}
all_uses_symbols = set.union(*uses_symbols.values(), set()}

Comment on lines 810 to 813
scope = routine
while scope:
import_map.update(scope.import_map)
scope = scope.parent
Copy link
Collaborator

Choose a reason for hiding this comment

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

Mind you: this poses the risk of overwriting a shadowed symbol - for example:

module a_mod
    use b_mod, only: a, b, c
contains
    subroutine a_routine
        use c_mod, only c
        ! symbol_map will now contain c => b_mod 
        ! because the parent scope is processed after the inner scope
    end subroutine a_routine
end module a_mod

One way to avoid this would be to use

import_map = CaseInsensitiveDict(
    (s.name, imprt) for imprt in routine.all_imports[::-1] for s in imprt.symbols
)

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.

Thanks! Unfortunately, there's still a bit left related to the imports, which may just have been an oversight. Otherwise good to go!

transformations/tests/test_data_offload.py Show resolved Hide resolved
transformations/transformations/data_offload.py Outdated Show resolved Hide resolved
@reuterbal reuterbal added the ready for merge This PR has been approved and is ready to be merged label Feb 6, 2024
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.

Only had a brief look, but looks very neat and well tested! GTG from me. :shipit:

@reuterbal reuterbal merged commit ff12ecb into main Feb 9, 2024
12 checks passed
@reuterbal reuterbal deleted the nams_global_var_hoisting branch February 9, 2024 12:09
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