-
Notifications
You must be signed in to change notification settings - Fork 13
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
handle modulo operator/function for c-like-backends #383
Conversation
Documentation for this branch can be viewed at https://sites.ecmwf.int/docs/loki/383/index.html |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #383 +/- ##
=======================================
Coverage 95.50% 95.50%
=======================================
Files 185 185
Lines 38756 38819 +63
=======================================
+ Hits 37013 37076 +63
Misses 1743 1743
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
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.
Thanks, I think this needs a rebase and subsequent import fix, the rest is just small nitpicks.
# pylint: disable=too-many-lines | ||
|
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.
We should start splitting the tests into logical units (but that can be a separate PR)
fcode = f""" | ||
subroutine transpile_special_functions(in, out) | ||
use iso_fortran_env, only: real64 | ||
implicit none | ||
{dtype}{'(kind=real64)' if dtype == 'real' else ''}, intent(in) :: in | ||
{dtype}{'(kind=real64)' if dtype == 'real' else ''}, intent(inout) :: out | ||
|
||
if (mod(in{'+ 2._real64' if add_float else ''}, 2{'._real64' if dtype == 'real' else ''}{'+ 0._real64' if add_float else ''}) .eq. 0) then | ||
out = 42{'._real64' if dtype == 'real' else ''} | ||
else | ||
out = 11{'._real64' if dtype == 'real' else ''} | ||
endif | ||
end subroutine transpile_special_functions |
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.
A suggestion for a slightly more readable code:
fcode = f""" | |
subroutine transpile_special_functions(in, out) | |
use iso_fortran_env, only: real64 | |
implicit none | |
{dtype}{'(kind=real64)' if dtype == 'real' else ''}, intent(in) :: in | |
{dtype}{'(kind=real64)' if dtype == 'real' else ''}, intent(inout) :: out | |
if (mod(in{'+ 2._real64' if add_float else ''}, 2{'._real64' if dtype == 'real' else ''}{'+ 0._real64' if add_float else ''}) .eq. 0) then | |
out = 42{'._real64' if dtype == 'real' else ''} | |
else | |
out = 11{'._real64' if dtype == 'real' else ''} | |
endif | |
end subroutine transpile_special_functions | |
if dtype == 'real': | |
decl_type = f'{dtype}(kind=real64)' | |
kind = f'._real64' | |
else: | |
decl_type = dtype | |
kind = '' | |
fcode = f""" | |
subroutine transpile_special_functions(in, out) | |
use iso_fortran_env, only: real64 | |
implicit none | |
{decl_type}, intent(in) :: in | |
{decl_type}, intent(inout) :: out | |
if (mod(in{'+ 2._real64' if add_float else ''}, 2{kind}{'+ 0._real64' if add_float else ''}) .eq. 0) then | |
out = 42{kind} | |
else | |
out = 11{kind} | |
endif | |
end subroutine transpile_special_functions |
14e7b37
to
1bc4706
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.
Looks great to me.
loki/backend/cgen.py
Outdated
@@ -140,6 +143,25 @@ def map_c_reference(self, expr, enclosing_prec, *args, **kwargs): | |||
def map_c_dereference(self, expr, enclosing_prec, *args, **kwargs): | |||
return self.format(' (*%s)', self.rec(expr.expression, PREC_NONE, *args, **kwargs)) | |||
|
|||
def map_inline_call(self, expr, enclosing_prec, *args, **kwargs): | |||
|
|||
class FindFloatLiterals(ExpressionFinder): |
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.
Just a minor nitpick, but I think this is general enough to live in loki.ir.expr_visitor.py
# instead of the floating-point version ('fmod') | ||
# whenever the mentioned evaluations result in being of kind 'integer' ... | ||
# as an example: 'celing(3.1415)' got an floating point value in it, however it evaluates/returns | ||
# an integer, in that case the wrong modulo function/operation is chosen |
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.
[no action] This is indeed a tricky corner case. Solution looks good for now, but might need some more thought when this gets problematic.
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.
Likely the correct approach would be an ExpressionTypeMapper
that attempts to determine the return type of an expression (similar to ExpressionDimensionsMapper
) - but it will require baking in a lot of knowledge about Fortran intrinsics. Fparser might provide some of that, so it could be feasible.
Definitely way beyond the scope of this PR, though.
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! We could also think about implementing a C++ (templated) function mymod()
which either calls fmod
or uses the modulo operator in dependence of the arguments types.
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, looks good to me.
@MichaelSt98: Since it'll take a little bit until the backlog of PRs is merged anyway, do you want to move the FindFloatLiterals
to the expr_visitor module and add a small test? Let me know, otherwise I'll green-light and merge.
# instead of the floating-point version ('fmod') | ||
# whenever the mentioned evaluations result in being of kind 'integer' ... | ||
# as an example: 'celing(3.1415)' got an floating point value in it, however it evaluates/returns | ||
# an integer, in that case the wrong modulo function/operation is chosen |
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.
Likely the correct approach would be an ExpressionTypeMapper
that attempts to determine the return type of an expression (similar to ExpressionDimensionsMapper
) - but it will require baking in a lot of knowledge about Fortran intrinsics. Fparser might provide some of that, so it could be feasible.
Definitely way beyond the scope of this PR, though.
@reuterbal Yes I'll move it to expr_visitor module and add a small test. Do we want to keep the name |
Good point! Real is better |
…rs.py and add corresponding tests
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.
That looks great now, many thanks!
[TRANSPILATION] handle modulo operator/function for c-like-backends ...
convert
MOD(a, b)
toa % b
for integer parameters/argumentsMOD(x, y)
tofmod(x, y)
for floating point parameters/arguments