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

cgen: return type and var for function(s) #269

Merged
merged 8 commits into from
Apr 10, 2024

Conversation

MichaelSt98
Copy link
Collaborator

cgen: return type and var for function(s)

Copy link

github-actions bot commented Apr 2, 2024

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

@MichaelSt98 MichaelSt98 force-pushed the nams_transpile_function_return branch from c5988a6 to 8f8ffca Compare April 3, 2024 09:43
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, in principle this looks very good. But the slight back-bending in cgen to determine return type and return variable suggests to me that we are actually leaving the IR in an inconsistent state for functions, and try to work around this now instead of fixing the original problem.

I've added a suggestion how this might be remedied. Of course it's untested and might unearth other issues, but let me know what you think.

Comment on lines 179 to 186
return_var = None
if o.is_function:
# Determine function result variable name
if not (result_name := o.result_name):
result_name = o.name.replace('_c', '')
if result_name in o.variable_map:
return_var = o.variable_map[result_name]
return_type = c_intrinsic_type(return_var.type) if return_var is not None else 'void'
Copy link
Collaborator

Choose a reason for hiding this comment

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

This reimplements the logic in

loki/loki/subroutine.py

Lines 324 to 332 in 31eabbe

def return_type(self):
"""
Return the return_type of this subroutine
"""
if not self.is_function:
return None
if self.result_name is not None:
return self.symbol_attrs.get(self.result_name)
return self.symbol_attrs.get(self.name)

but handles the special case that the function name no longer matches the name of the return variable. But that seems like an inconsistent IR state.

I would suggest to instead handle that renaming in the f2c transformation before this line:

kernel.name = f'{kernel.name.lower()}_c'

with something like this:

if kernel.is_function and kernel.result_name is None:
    kernel.result_name = kernel.name

I think that should allow you to simply write here then

Suggested change
return_var = None
if o.is_function:
# Determine function result variable name
if not (result_name := o.result_name):
result_name = o.name.replace('_c', '')
if result_name in o.variable_map:
return_var = o.variable_map[result_name]
return_type = c_intrinsic_type(return_var.type) if return_var is not None else 'void'
if o.is_function:
return_type = c_intrinsic_type(o.return_type)
else:
return_type = 'void'

@@ -183,7 +194,11 @@ def visit_Subroutine(self, o, **kwargs):

# Fill the body
body += [self.visit(o.body, **kwargs)]
body += [self.format_line('return 0;')]
# body += [self.format_line('return 0;')]
Copy link
Collaborator

Choose a reason for hiding this comment

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

debug leftover?

Comment on lines 200 to 201
if return_var is not None:
body += [self.format_line(f'return {return_var.name.lower()};')]
Copy link
Collaborator

Choose a reason for hiding this comment

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

With the above change, this would then become:

Suggested change
if return_var is not None:
body += [self.format_line(f'return {return_var.name.lower()};')]
if o.result_name is not None:
body += [self.format_line(f'return {o.result_name.lower()};')]

Copy link

codecov bot commented Apr 9, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 92.85%. Comparing base (31eabbe) to head (1bdb019).
Report is 12 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #269      +/-   ##
==========================================
+ Coverage   92.82%   92.85%   +0.03%     
==========================================
  Files         102      102              
  Lines       18093    18158      +65     
==========================================
+ Hits        16795    16861      +66     
+ Misses       1298     1297       -1     
Flag Coverage Δ
lint_rules 96.39% <ø> (ø)
loki 92.82% <100.00%> (+0.02%) ⬆️
transformations 92.20% <ø> (+0.10%) ⬆️

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.

body += [self.format_line('return 0;')]

# if something to be returned, add 'return <var>' statement
if o.result_name is not None:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do we want to double-check wether o.is_function?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good point, since we can now rely on o.result_name to always have a sensible value for functions, we don't have to check for result_name but can rather check for is_function

@MichaelSt98
Copy link
Collaborator Author

Within FortranCTransformation

if kernel.is_function and kernel.result_name is None:
    kernel.result_name = kernel.name

not necessary anymore, since result_name is now set by default if is_function=True

body += [self.format_line('return 0;')]

# if something to be returned, add 'return <var>' statement
if o.result_name is not None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Good point, since we can now rely on o.result_name to always have a sensible value for functions, we don't have to check for result_name but can rather check for is_function

loki/subroutine.py Show resolved Hide resolved
@MichaelSt98 MichaelSt98 force-pushed the nams_transpile_function_return branch from e15f05c to f705ad4 Compare April 10, 2024 11:34
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.

Many thanks, this looks great now!

@mlange05: This implies a small IR change in the sense that functions will always record the name of the variable that contains the return value in result_name. That makes accessing this information much easier without the need to figure out which of the multiple variants of defining the result variable was used in the original Fortran.
For language compliance, the backend will only add the corresponding RESULT(<...>) appendix to the function statement if the name differs from the function name, thus retaining the original behaviour.
A positive side-effect of this is that renaming of functions can now happen without the need to update the result variable's name throughout the entire function body.
Let us know if you're happy with this!

@reuterbal reuterbal added the ready for merge This PR has been approved and is ready to be merged label Apr 10, 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.

Yes, very happy with the IR / behaviour change (was planning on something like that for the eventual procedure overhaul). Great stuff, GTG from me, and thanks both! 🙏 :shipit:

@reuterbal reuterbal merged commit 2b9ab70 into main Apr 10, 2024
12 checks passed
@reuterbal reuterbal deleted the nams_transpile_function_return branch April 10, 2024 14:25
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