-
Notifications
You must be signed in to change notification settings - Fork 12
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
Recursive inlining via InlineTransform and associated fixes #205
Conversation
Documentation for this branch can be viewed at https://sites.ecmwf.int/docs/loki/205/index.html |
bac2a6d
to
5a2c7ed
Compare
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #205 +/- ##
==========================================
- Coverage 92.26% 92.26% -0.01%
==========================================
Files 96 95 -1
Lines 17132 17176 +44
==========================================
+ Hits 15807 15847 +40
- Misses 1325 1329 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
453347b
to
071e9dc
Compare
071e9dc
to
ef8a5cd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great and is much appreciated!
I left a few comments while browsing the changes, most of which are not strictly necessary to act upon other than some massaging of docstrings (marking this as Request changes for that). One thing to consider, though, would be to add a test for the new SanitiseTransformation
to the Loki core's test base. It's currently only being invoked from the tests in the transformations sub-package.
loki/transform/transform_inline.py
Outdated
] | ||
|
||
|
||
class InlineTransformation(Transformation): | ||
""" | ||
:any:`Transformation` object to apply several types of source inlining |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<pedantic>
:any:`Transformation` object to apply several types of source inlining | |
:any:`Transformation` class to apply several types of source inlining |
</pedantic>
loki/transform/transform_inline.py
Outdated
inline_constants : bool | ||
Replace instances of variables with known constant values by | ||
:any:`Literal`; default: False. | ||
inline_elementals : bool | ||
Replaces :any:`InlineCall` expression to elemental functions | ||
with the called function's body; default: True. | ||
inline_internals : bool | ||
Perform internal procedure inlining via | ||
:method:`inline_internal_procedures`; default: False. | ||
inline_marked : bool | ||
Inline :any:`Subroutine` objects marked by pragma annotations; | ||
default: True. | ||
eliminate_dead_code : bool | ||
Perform dead code elimination, where unreachable branches are | ||
trimmed from the code; default@ True | ||
allowed_aliases : tuple or list of str or :any:`Expression`, optional | ||
List of variables that will not be renamed in the parent scope during | ||
internal and pragma-driven inlining. | ||
remove_imports : bool | ||
Strip unused import symbols after pragma-inlining (optional, default: True) | ||
external_only : bool, optional | ||
Do not replace variables declared in the local scope when | ||
inlining constants (default: True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a readability point of view, it could be useful to provide a cross-link to the respective utility function, e.g.:
inline_constants : bool
Replace instances of variables with known constant values by :any:`Literal` (see :any:`inline_constant_parameters`); default: False.
That'll generate a direct link in the HTML docs to read up on the specific utility's behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes!
loki/transform/transform_inline.py
Outdated
Notes | ||
----- | ||
The ``.type.initial`` property is used to derive the replacement | ||
value,a which means for symbols imported from external modules, | ||
the parent :any:`Module` needs to be supplied in the | ||
``definitions`` to the constructor when creating :param:`routine`. | ||
|
||
Variables that are replaced are also removed from their | ||
corresponding import statements, with empty import statements | ||
being removed alltogether. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to come before Parameters
; Sphinx disregards anything after Parameters and/or Returns.
@@ -179,7 +268,10 @@ def inline_elemental_functions(routine): | |||
|
|||
exprmap = {} | |||
for call in FindInlineCalls().visit(routine.body): | |||
if call.procedure_type is not BasicType.DEFERRED: | |||
if call.procedure_type is BasicType.DEFERRED: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conceptual question, not necessary to act upon here: Going forward, should we try to include debug output (or a new "optimisation report"-like output stream) in situations like this, where a transformation is not applied because an assumption isn't met (here: missing enrichment information).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'm planning an overhaul of the logging from transformations once the configuration changes are more complete (so we can select log-leve per trafo, etc.).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly worth capturing via an issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loki/transform/transform_inline.py
Outdated
@@ -193,7 +285,7 @@ def inline_elemental_functions(routine): | |||
# Remove all module imports that have become obsolete now | |||
import_map = {} | |||
for im in FindNodes(Import).visit(routine.spec): | |||
if all(hasattr(s, 'type') and s.type.dtype in removed_functions for s in im.symbols): | |||
if im.symbols and all(hasattr(s, 'type') and s.type.dtype in removed_functions for s in im.symbols): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the hasattr(s, 'type')
should no longer be necessary - that is a remnant of the times when we represented the symbol list in Import
with names as strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will try, but need to rebase this over ec-phys regression...
# When inlining we allow the horizontal dimension to alias, so that | ||
# the de/re-vectorisation captures the shared vector dimension. | ||
inline_marked_subroutines(routine, allowed_aliases=(self.horizontal.index,)) | ||
|
||
# Associates at the highest level, so they don't interfere | ||
# with the sections we need to do for detecting subroutine calls | ||
resolve_associates(routine) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for not including resolve_associates
in the SanitiseTransformation
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oversight, now fixed (I hope).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scratch that - better answer is: Because EC-physics regression breaks! 😭 There seems to be some unfortunate side-effects with the SCC-vector demotion if this is executed before the Inliner - which this implicitly does.
I'm planning a more genera refactoring of the individual steps where this and some other steps would be brought forward into the "sanitise pass". Would you mind if we hold back on this change until then, @reuterbal ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I only asked for the reason, and that's a good enough one ;-)
ef8a5cd
to
3e86a96
Compare
Ok, I rebased and pushed the fixes to the suggestions - many thanks @reuterbal . I'll have to rebase/re-run this with ec-phys regression, just to make sure, but will ping here once confirmed. |
e44f43f
to
654193e
Compare
As the removal of calls can make import statements obsolete, we add an option to remove this. This can help with keeping the call-tree clean.
To separately configure and apply recursive source inlining for different entities, we add this Transformation and according test.
This so far includes resolving associates and sequence associations, but could be extended into a general-purpose input unification pipeline.
654193e
to
63a4434
Compare
Ok, rebased, CI happy and confirmed with EC-physics regression. I've also added test for the new trafo and addressed all comments, except the breakage one. I think this is ready for another look, @reuterbal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many thanks, changes look good to me and everything has been addressed. GTG!
@@ -179,7 +268,10 @@ def inline_elemental_functions(routine): | |||
|
|||
exprmap = {} | |||
for call in FindInlineCalls().visit(routine.body): | |||
if call.procedure_type is not BasicType.DEFERRED: | |||
if call.procedure_type is BasicType.DEFERRED: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# When inlining we allow the horizontal dimension to alias, so that | ||
# the de/re-vectorisation captures the shared vector dimension. | ||
inline_marked_subroutines(routine, allowed_aliases=(self.horizontal.index,)) | ||
|
||
# Associates at the highest level, so they don't interfere | ||
# with the sections we need to do for detecting subroutine calls | ||
resolve_associates(routine) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I only asked for the reason, and that's a good enough one ;-)
This PR separates the various styles of source-inlining (elementals, internals, pragma-marked) into a dedicated
Transformation
class that can be configured independently. This key motivation for this is that this allows us to perform recursive multi-step inlining, where individual inlining steps are performed at the leaves first and then propagated up by traversing theScheduler
tree in reverse order.As these steps where previously baked into the
SCCBaseTransformation
several of these steps have now been removed. Due to complex interplay in the ec-physics regression, things like associate resolution and dead-code-elimination have to now also be performed with the inlining steps. Until we have proper pipeline configuration, I have chosen to go the simple route and simply lump these in with theInlineTransformation
, but would appreciate feedback on this. This refactoring also has show some (severe!) problems with the previous "elemental function" inliner, which are also fixed in this PR.Edit: As discussed offline, I've also added a first draft of a "housekeeping" transformation (
SanitiseTransformation
), for a easily configurable code harmonisation utility pass. I plan to extend this as we progress through the trafor cleanup and consolidation.Edit: Turns out one of the small fixes actually has a some far-reaching consequences: The elemental inliner was originally not checking for true elemental functions, and it now does(!), but the problem is that our OFP frontend cannot deal with prefix keywords. As the elemental inliner is needed for C-transpilation though, this means a bunch of tests, including CLOUDSC, have to be disabled for OFP. I've had a go at retro-fitting this feature, but it's quite involed due to the broken scoping around type nodes in the OFP AST. I propose dropping OFP entirely now, but I'll track that as a separate issue.