Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

optimization: evaluating function calls in logical expressions

445 views
Skip to first unread message

Janus

unread,
May 2, 2018, 8:51:21 AM5/2/18
to
Hi all,

please consider this simple example code:


program lazy

logical :: flag

flag = .false.
flag = check() .and. flag
flag = flag .and. check()

contains

logical function check()
integer, save :: i = 1
print *, "check", i
i = i + 1
check = .true.
end function

end


This example contains two calls of the function 'check', and the question is whether the compiler is allowed to optimize any of those calls away (they don't influence the resulting value of 'flag', but they do have side effects!).

The compilers I have access to are split into two camps by this questions:

1) ifort, flang and pgfortran evaluate the function twice (even with -O3) and print ...
check 1
check 2

2) gfortran and sunf95 on the other hand get rid of the second call and print ...
check 1


Is this a valid optimization, or does the Fortran standard forbid this? Does the answer change if I make the function pure?

pure logical function check()
check = .true.
end function


Note that I have already reported this in the GCC bugzilla (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85599), but it is not fully clear to me whether it is actually a bug in gfortran or not. See the link for some discussion.

Cheers,
Janus

Arjen Markus

unread,
May 2, 2018, 9:59:19 AM5/2/18
to
I may be mistaken, but I am sure that the reported behaviour is completely conforming: the Fortran standard does not demand shortcircuiting, but does not exclude it either.

That is: if ( allocated(array) .and. array(1) > 0 ) then
is not guaranteed to work.

Similar for side effects: you do not know in which order functions are evaluated, hence you should not rely on the order.

a = f(b) + f(c)

might be evaluated with f(b) first and then f(c) or vice versa. The mathematical interpretation is leading.

Regards,

Arjen

Thomas Jahns

unread,
May 2, 2018, 10:12:35 AM5/2/18
to
On 05/02/18 15:59, Arjen Markus wrote:
> That is: if ( allocated(array) .and. array(1) > 0 ) then
> is not guaranteed to work.

there is only two cases to consider: array is always allocated to include index
1 and the first part is redundant or array is not allocated (to include index 1)
and the second part is invoking undefined behaviour, i.e. if the allocation
status of array cannot be assumed to be a given, the whole program is not Fortran.

> Similar for side effects: you do not know in which order functions are evaluated, hence you should not rely on the order.
>
> a = f(b) + f(c)
>
> might be evaluated with f(b) first and then f(c) or vice versa. The mathematical interpretation is leading.

I'm not even sure Fortran makes guarantees about side-effects of f here, i.e.
any f having side-effects might be invalid to call in this way and one may not
make assumptions about ordering as such. Because of e.g. inlining the executions
of f(b) and f(c) might happen concurrently, and the program behaviour is only
correct if all executions of f(b) and f(c) yield the same result.

Thomas




Janus

unread,
May 2, 2018, 10:36:48 AM5/2/18
to
Hi Arjen,

thanks for your reply.


> I may be mistaken, but I am sure that the reported behaviour is completely conforming: the Fortran standard does not demand shortcircuiting, but does not exclude it either.

So you are saying that the Fortran standard does not make any statement about what should happen in this case, and it's completely up to the implementation to do as it pleases?

That sounds a bit unsatisfying to me, because it clearly leads to various compilers yielding different results on the same code (which somehow defeats the purpose of a standards document).


My naive expectation was that the Fortran standard should forbid the removal of the function call, in particular since the function does have side effects (which are moderate in the example, but might be more dramatic in general).

The most relevant quote from F2008 seems to be the following to me:

*****
7.1.4 Evaluation of operations
2 The evaluation of a function reference shall neither affect nor be affected by the evaluation of any other entity within the statement.
*****

I interpret this such that the function call cannot be omitted, because that would mean that the evaluation of the function is affected by the other operand: "flag = flag .and. check()".

Can we agree on this, or am I reading this sentence wrongly? This interpretation would imply that PURE function calls can be omitted, but non-pure ones can not ...

Cheers,
Janus

Thomas Jahns

unread,
May 2, 2018, 12:01:26 PM5/2/18
to
On 05/02/18 16:36, Janus wrote:
> The most relevant quote from F2008 seems to be the following to me:
>
> *****
> 7.1.4 Evaluation of operations
> 2 The evaluation of a function reference shall neither affect nor be affected by the evaluation of any other entity within the statement.
> *****
>
> I interpret this such that the function call cannot be omitted, because that would mean that the evaluation of the function is affected by the other operand: "flag = flag .and. check()".
>
> Can we agree on this, or am I reading this sentence wrongly? This interpretation would imply that PURE function calls can be omitted, but non-pure ones can not ...

Yep, you are reading too much into that: rather it's up to you to make sure that
a function reference does not have side effects that affect the outcome of the
expression. I.e. instead of additional guarantees, this paragraph places
additional constraints on what valid Fortran code may do.

Thomas


Ron Shepard

unread,
May 2, 2018, 12:05:24 PM5/2/18
to
On 5/2/18 9:36 AM, Janus wrote:
> The most relevant quote from F2008 seems to be the following to me:
>
> *****
> 7.1.4 Evaluation of operations
> 2 The evaluation of a function reference shall neither affect nor be affected by the evaluation of any other entity within the statement.
> *****
>
> I interpret this such that the function call cannot be omitted, because that would mean that the evaluation of the function is affected by the other operand: "flag = flag .and. check()".
>
> Can we agree on this, or am I reading this sentence wrongly? This interpretation would imply that PURE function calls can be omitted, but non-pure ones can not ...

I am not certain about the answer to your question. This is an issue
that comes up here frequently, and as far as I know, the language
experts disagree with each other.

My interpretation of that sentence in the standard says that the
programmer is not allowed to invoke those functions within statements.
This could be invoking the same function multiple times within a
statement, but it could also apply to invoking different functions
within the same statement. The clue is that the word "shall" is used.
That word is used to define restrictions on the programmer, not on the
compiler.

I think your interpretation that this somehow requires the function to
be invoked regardless of its side effects is incorrect. It does not
define in any way what a compiler is required or allowed to do.

However, it isn't clear to me that this statement applies to your
situation. In your situation the function is invoked only once in each
statement, and you are asking if optimizations are allowed that
eliminate the executions of whole statements. I don't know the answer to
that, and I don't think there is consensus on that issue among the
language experts. Fortran is allowed to rearrange statements in ways
that are mathematically equivalent, and this might be an example of such
an allowed rearrangement. It is also allowed to evaluate common
subexpressions across multiple statements and to eliminate dead code
during optimization, and this might be an example of that kind of
optimization.

I do not think the pure attribute changes any of this. It is still a
restriction on the programmer, not the compiler, so the pure attribute
does not seem to affect any of that. That is, if a function is pure
(meaning no side effects in this context), then the same restrictions
apply to the programmer whether or not the pure attribute is used within
the function definition. (Of course, there are other situations, e.g.
involving parallel execution, where the language requires the pure
attribute, but I don't think this is one of them.)

BTW, the language authors know about this ambiguity. It does not exist
because no one has ever noticed it before and it slipped through the
cracks all this time. It exists because some believe the compiler should
be allowed to make such optimizations, and they prevent any changes to
the language that would prevent them. So the ambiguity serves a purpose,
it is not just incidental.

If you want to avoid all of this ambiguity, then don't use functions
with side effects like this. Use subroutines instead. That is exactly
what they are for. There is no ambiguity like this associated with
subroutines.

$.02 -Ron Shepard

Janus

unread,
May 2, 2018, 1:04:08 PM5/2/18
to
On Wednesday, May 2, 2018 at 6:05:24 PM UTC+2, Ron Shepard wrote:
> On 5/2/18 9:36 AM, Janus wrote:
> > The most relevant quote from F2008 seems to be the following to me:
> >
> > *****
> > 7.1.4 Evaluation of operations
> > 2 The evaluation of a function reference shall neither affect nor be affected by the evaluation of any other entity within the statement.
> > *****
> >
> > I interpret this such that the function call cannot be omitted, because that would mean that the evaluation of the function is affected by the other operand: "flag = flag .and. check()".
> >
> > Can we agree on this, or am I reading this sentence wrongly? This interpretation would imply that PURE function calls can be omitted, but non-pure ones can not ...
>
> I am not certain about the answer to your question. This is an issue
> that comes up here frequently, and as far as I know, the language
> experts disagree with each other.
>
> My interpretation of that sentence in the standard says that the
> programmer is not allowed to invoke those functions within statements.
> This could be invoking the same function multiple times within a
> statement, but it could also apply to invoking different functions
> within the same statement. The clue is that the word "shall" is used.
> That word is used to define restrictions on the programmer, not on the
> compiler.
>
> I think your interpretation that this somehow requires the function to
> be invoked regardless of its side effects is incorrect. It does not
> define in any way what a compiler is required or allowed to do.

Huh, that's too bad :(


> However, it isn't clear to me that this statement applies to your
> situation. In your situation the function is invoked only once in each
> statement, and you are asking if optimizations are allowed that
> eliminate the executions of whole statements. I don't know the answer to
> that, and I don't think there is consensus on that issue among the
> language experts.

Even worse. Might be a good idea to try and reach consensus for the upcoming Fortran 2018 standard ...?


> Fortran is allowed to rearrange statements in ways
> that are mathematically equivalent, and this might be an example of such
> an allowed rearrangement. It is also allowed to evaluate common
> subexpressions across multiple statements and to eliminate dead code
> during optimization, and this might be an example of that kind of
> optimization.

Well, if you're only looking at the end result of the expression, then rearrangement and omission might not be a problem. But then the expression result is not the only thing, and there might be side effects (I/O, global variables, etc), which in turn could even influence the expression result. That's why I thought that the PURE attribute might be relevant, because it implies that the function is side-effect free.


> I do not think the pure attribute changes any of this. It is still a
> restriction on the programmer, not the compiler, so the pure attribute
> does not seem to affect any of that. That is, if a function is pure
> (meaning no side effects in this context), then the same restrictions
> apply to the programmer whether or not the pure attribute is used within
> the function definition. (Of course, there are other situations, e.g.
> involving parallel execution, where the language requires the pure
> attribute, but I don't think this is one of them.)

Actually I'm not so much interested in the restrictions on the programmer here, but rather in the restrictions on the compiler (regarding optimizations etc).


> BTW, the language authors know about this ambiguity. It does not exist
> because no one has ever noticed it before and it slipped through the
> cracks all this time. It exists because some believe the compiler should
> be allowed to make such optimizations, and they prevent any changes to
> the language that would prevent them. So the ambiguity serves a purpose,
> it is not just incidental.

Ambiguity in the standard serves a purpose? You totally lost me here ...


> If you want to avoid all of this ambiguity, then don't use functions
> with side effects like this. Use subroutines instead. That is exactly
> what they are for. There is no ambiguity like this associated with
> subroutines.

Again, this is not about me as a user wanting to write good code, but rather about what the compiler should do when it encounters such code.

Since the Fortran standard allows people to use functions with side effects, it should make clear how they are to be evaluated.

Cheers,
Janus

Thomas Koenig

unread,
May 2, 2018, 1:18:37 PM5/2/18
to
Janus <ja...@gcc.gnu.org> schrieb:

> The most relevant quote from F2008 seems to be the following to me:
>
> *****
> 7.1.4 Evaluation of operations
> 2 The evaluation of a function reference shall neither affect
> nor be affected by the evaluation of any other entity within
> the statement.

The critical question here is, does "evaluation" mean that
the function has to be called, or not?

Unfortunately, "evaluation" is not among the terms defined by the
standard itself. Taken literally, "evaluation" means "determining
the value of", so in this case, the sentence you quoted would mean
that the function need not be called.

So, what else is there... Ah, 7.1.7 and note 7.28.

# It is not necessary for a processor to evaluate all of the operands
# of an expression, or to evaluate entirely each operand, if the
# value of the expression can be determined otherwise.

# This principle is most often applicable to logical expressions,
# zero-sized arrays, and zero-length strings, but it applies to
# all expressions.

# For example, in evaluating the expression

# X > Y .OR. L (Z)

# where X, Y, and Z are real and L is a function of type logical,
# the function reference L (Z) need not be evaluated if X is greater
# than Y.

I think that makes it clear - gfortran is correct in doing what
it does.

Ian Harvey

unread,
May 2, 2018, 11:26:41 PM5/2/18
to
On 2018-05-03 02:34, Janus wrote:
> On Wednesday, May 2, 2018 at 6:05:24 PM UTC+2, Ron Shepard wrote:

...
> Well, if you're only looking at the end result of the expression,
> then rearrangement and omission might not be a problem. But then the
> expression result is not the only thing, and there might be side
> effects (I/O, global variables, etc), which in turn could even
> influence the expression result. That's why I thought that the PURE
> attribute might be relevant, because it implies that the function is
> side-effect free.

As an aside, PURE procedures are only side effect free from the point of
view of the immediate value of other variables after the procedure
returns. There are a number of other observable side effects, including
rather obvious ones such as termination of the program, which are
permitted.

>> I do not think the pure attribute changes any of this. It is still
>> a restriction on the programmer, not the compiler, so the pure
>> attribute does not seem to affect any of that. That is, if a
>> function is pure (meaning no side effects in this context), then
>> the same restrictions apply to the programmer whether or not the
>> pure attribute is used within the function definition. (Of course,
>> there are other situations, e.g. involving parallel execution,
>> where the language requires the pure attribute, but I don't think
>> this is one of them.)
>
> Actually I'm not so much interested in the restrictions on the
> programmer here, but rather in the restrictions on the compiler
> (regarding optimizations etc).
>
>
>> BTW, the language authors know about this ambiguity. It does not
>> exist because no one has ever noticed it before and it slipped
>> through the cracks all this time. It exists because some believe
>> the compiler should be allowed to make such optimizations, and they
>> prevent any changes to the language that would prevent them. So the
>> ambiguity serves a purpose, it is not just incidental.
>
> Ambiguity in the standard serves a purpose? You totally lost me here
> ...

Ambiguity is perhaps the wrong word. The standard provides for freedom
of behaviour of implementations within limits. That freedom permits
additional optimisation by implementations, if they deem it worthwhile.

>> If you want to avoid all of this ambiguity, then don't use
>> functions with side effects like this. Use subroutines instead.
>> That is exactly what they are for. There is no ambiguity like this
>> associated with subroutines.
>
> Again, this is not about me as a user wanting to write good code, but
> rather about what the compiler should do when it encounters such
> code.
>
> Since the Fortran standard allows people to use functions with side
> effects, it should make clear how they are to be evaluated.

My view is that the original program is non-conforming, based on the
sections of text mentioned by Thomas Koenig elsethread (F2008 7.1.7)
that give freedom to implementations around evaluation of operations,
and following restrictions that mean that the attempt to observe the
number of times that check is called by referencing the value of i is
not permitted.

I don't think the example in the original post is particularly contentious.

Ron Shepard

unread,
May 3, 2018, 3:38:15 AM5/3/18
to
On 5/2/18 12:04 PM, Janus wrote:
>> BTW, the language authors know about this ambiguity. It does not exist
>> because no one has ever noticed it before and it slipped through the
>> cracks all this time. It exists because some believe the compiler should
>> be allowed to make such optimizations, and they prevent any changes to
>> the language that would prevent them. So the ambiguity serves a purpose,
>> it is not just incidental.
> Ambiguity in the standard serves a purpose? You totally lost me here ...

The standard allows certain optimizations to be performed, but it does
not require them. Therefore what a compiler actually does is ambiguous.
Also, what it does may depend on compiler options, such as the
optimization level.

>
>> If you want to avoid all of this ambiguity, then don't use functions
>> with side effects like this. Use subroutines instead. That is exactly
>> what they are for. There is no ambiguity like this associated with
>> subroutines.
> Again, this is not about me as a user wanting to write good code, but rather about what the compiler should do when it encounters such code.

That is what I explained above, what the compiler does.

> Since the Fortran standard allows people to use functions with side effects, it should make clear how they are to be evaluated.

It doesn't in some cases, and that is by design, as explained above. It
is not going to change now, not after some 50 years of momentum.
Instead, what it does is restrict the programmer from using functions
with side effects in certain ways.

Someone mentioned logical expressions, but this also applies to
arithmetic functions. For example, the expression

x * f()

can be evaluated without the actual function call when x==0.0. Such a
test might be invoked in a static way at compile time, or even at
runtime in a dynamic way, or, as I mentioned above, whether and how the
test occurs might depend on the optimization level.

$.02 -Ron Shepard

Thomas Jahns

unread,
May 3, 2018, 3:57:41 AM5/3/18
to
On 05/03/18 09:38, Ron Shepard wrote:
> Someone mentioned logical expressions, but this also applies to arithmetic
> functions.  For example, the expression
>
>    x * f()
>
> can be evaluated without the actual function call when x==0.0. Such a test might
> be invoked in a static way at compile time, or even at runtime in a dynamic way,
> or, as I mentioned above, whether and how the test occurs might depend on the
> optimization level.

not true on most platforms (i.e. those with IEEE 754 floating point): 0.0 * NaN
results in NaN, not 0.0.

Thomas
Message has been deleted

robin....@gmail.com

unread,
May 3, 2018, 6:59:13 AM5/3/18
to
On Wednesday, May 2, 2018 at 11:59:19 PM UTC+10, Arjen Markus wrote:

> I may be mistaken, but I am sure that the reported behaviour is completely conforming: the Fortran standard does not demand shortcircuiting, but does not exclude it either.
>
> That is: if ( allocated(array) .and. array(1) > 0 ) then
> is not guaranteed to work.

Unless array is allocated, the statement will usually crash.

robin....@gmail.com

unread,
May 3, 2018, 7:09:03 AM5/3/18
to
On Thursday, May 3, 2018 at 12:36:48 AM UTC+10, Janus wrote:

> > I may be mistaken, but I am sure that the reported behaviour is completely conforming: the Fortran standard does not demand shortcircuiting, but does not exclude it either.
>
> So you are saying that the Fortran standard does not make any statement about what should happen in this case,

What will happen is that an attempt will be made to evaluate
the two sub-expressions (i.e., the operands of the .and. operator).
The evaluations may be carried out in any order.

> and it's completely up to the implementation to do as it pleases?

No, see above.

> That sounds a bit unsatisfying to me, because it clearly leads to various compilers yielding different results on the same code (which somehow defeats the purpose of a standards document).

Different compilers can normally produce different results.
Nothing different here.

Arjen Markus

unread,
May 3, 2018, 7:20:27 AM5/3/18
to
Yes :). Unless some sort of short-circuiting in action. But the Fortrsn standard does not prescribe that (unlike some other languages like C). So this needs to be written as:
if ( allocated(array) ) then
if ( array(1) > 0 ) then
...

Regards,

Arjen

kargl

unread,
May 3, 2018, 9:47:09 AM5/3/18
to
If a user isn't using the IEEE modules as described in the standard,
0. * NaN is invalid unless you argument is a member of the
representable numbers on the processor.

--
steve

Ron Shepard

unread,
May 3, 2018, 8:39:31 PM5/3/18
to
Even the "same" compiler with different options, such as optimization
levels, can produce different results. This in an inherent issue of
using functions with side effects.

$.02 -Ron Shepard

Ron Shepard

unread,
May 3, 2018, 9:08:33 PM5/3/18
to
Perhaps I should have used another arithmetic example, such as

i * f()

My point was that if the compiler can see that i==0 as this expression
is encountered, then it might optimize away the function reference
entirely. That is, if you look at the load map or at external symbol
references, there may be no symbol corresponding to the function f().
This might apply to the code sequence

i = 0
j = i * f()

which might be replaced by the optimizer with the simpler

i = 0
j = 0

On the other hand, if it does not know the value of i at compile time,
then it might conditionally invoke the function "as if" the expression
had been written

if ( i == 0 ) then
j = 0
else
j = i * f()
endif

In this case, any side effects associated with the evaluation of f()
might not be observed, or the number of f() evaluations might not match
expectations.

I do have to admit that I have never quite understood when such
optimizations are allowed and when they aren't. I just know that this
has been discussed here in clf for several decades, with no consensus
among the language experts. I also know, from previous clf dicsussions,
that this is probably why the intrinsic random number generator is
implemented as a subroutine rather than as a function, as the previous
extensions by various vendors had almost always chosen to do. That
change of semantics cleanly sidesteps these issues by avoiding these
ambiguities, and there was no need to make an intrinsic function that
behaved differently than user-written functions regarding this issue of

Janus

unread,
May 6, 2018, 2:30:25 PM5/6/18
to
On Thursday, May 3, 2018 at 5:26:41 AM UTC+2, Ian Harvey wrote:
> My view is that the original program is non-conforming, based on the
> sections of text mentioned by Thomas Koenig elsethread (F2008 7.1.7)
> that give freedom to implementations around evaluation of operations,

These sections as such seem suggest that the code is standard conforming and that the compiler has some freedom of implementation.


> and following restrictions that mean that the attempt to observe the
> number of times that check is called by referencing the value of i is
> not permitted.

Are you talking about paragraph (2) in section 7.1.7, which says:

"If a statement contains a function reference in a part of an expression that need not be evaluated, all entities that would have become defined in the execution of that reference become undefined at the completion of evaluation of the expression containing the function reference."

If I interpret that in your sense (which seems possible), that again leads me to a quite unsatisfying situation:
I have a simple code example that looks quite innocent at the face of it (see my original post). The Fortran standard says it's non-conforming, but apparently it does not require the compiler to diagnose this. Of course such a diagnostic is not trivial to implement in the general case, and no compiler actually seems to do it. Finally it's up to the programmer to make sure that such a situation does not arise in his code.

I bet most people writing Fortran code are actually not aware of this restriction (at least I wasn't), and even if they are, it's quite easy to miss it or forget about it. So we end up with many people writing non-conforming code and compilers silently swallowing it and producing 'arbitrary' results.

I find that quite unsatisfying, because IMHO the Fortran standard should ideally define things in a non-ambiguous way so that every compiler (that claims to be compatible with the standard) is required to produce a well-defined result, or reject the code if it is non-conforming.

It's clear that there are limits to this ideal conception, but in the case at hand all of this mess could have been avoided, if the Fortran standard would simply forbid that a function call with side effects (i.e. non-pure) is optimized out. Full stop. No freedom of implementation. No undiagnosable restrictions.

Cheers,
Janus

Thomas Koenig

unread,
May 6, 2018, 3:41:37 PM5/6/18
to
Janus <ja...@gcc.gnu.org> schrieb:

> I have a simple code example that looks quite innocent at the
> face of it (see my original post). The Fortran standard says it's
> non-conforming, but apparently it does not require the compiler
> to diagnose this.

That is, indeed, the case.

> Of course such a diagnostic is not trivial to
> implement in the general case, and no compiler actually seems to
> do it. Finally it's up to the programmer to make sure that such
> a situation does not arise in his code.

Yes.

Of course, an optimization to change something like

if (variable .and. function()) then

to

if (variable) then
if (function()) then
...

or even

if (function() .and. variable)

into

if (variable) then
if (function()) then

is quite doable. It is also doable to add warn about this kind
of thing, so add this to the myriad of warnings that are already
in place in leading compilers :-)

[...]

> It's clear that there are limits to this ideal conception,
> but in the case at hand all of this mess could have been avoided,
> if the Fortran standard would simply forbid that a function call
> with side effects (i.e. non-pure) is optimized out. Full stop. No
> freedom of implementation. No undiagnosable restrictions.

Sure, but there are more traps than that. Fortran has very many
rules which are supposed to aid optimization and which can be a
trap for users.

Ron Shepard

unread,
May 6, 2018, 7:49:38 PM5/6/18
to
On 5/6/18 1:30 PM, Janus wrote:
> I find that quite unsatisfying, because IMHO the Fortran standard should ideally define things in a non-ambiguous way so that every compiler (that claims to be compatible with the standard) is required to produce a well-defined result, or reject the code if it is non-conforming.

That is fine, except in the typical case, the compiler cannot know what
side effects, if any, are associated with evaluation of an external
function. So you are asking for something that is not possible.
Instead, the compiler puts the burden of conformance on the programmer.

> It's clear that there are limits to this ideal conception, but in the case at hand all of this mess could have been avoided, if the Fortran standard would simply forbid that a function call with side effects (i.e. non-pure) is optimized out. Full stop. No freedom of implementation. No undiagnosable restrictions.

I have explained before, a new requirement to disallow optimizations
that have been allowed historically for 50+ years is not going to pass
muster.

For new programmers, an easy rule to follow is to avoid functions with
side effects; use subroutines instead.

$.02 -Ron Shepard

robin....@gmail.com

unread,
May 6, 2018, 11:47:15 PM5/6/18
to
On Monday, May 7, 2018 at 4:30:25 AM UTC+10, Janus wrote:
> On Thursday, May 3, 2018 at 5:26:41 AM UTC+2, Ian Harvey wrote:
> > My view is that the original program is non-conforming, based on the
> > sections of text mentioned by Thomas Koenig elsethread (F2008 7.1.7)
> > that give freedom to implementations around evaluation of operations,
>
> These sections as such seem suggest that the code is standard conforming and that the compiler has some freedom of implementation.
>
>
> > and following restrictions that mean that the attempt to observe the
> > number of times that check is called by referencing the value of i is
> > not permitted.
>
> Are you talking about paragraph (2) in section 7.1.7, which says:
>
> "If a statement contains a function reference in a part of an expression that need not be evaluated, all entities that would have become defined in the execution of that reference become undefined at the completion of evaluation of the expression containing the function reference."
>
> If I interpret that in your sense (which seems possible), that again leads me to a quite unsatisfying situation:
> I have a simple code example that looks quite innocent at the face of it (see my original post). The Fortran standard says it's non-conforming, but apparently it does not require the compiler to diagnose this. Of course such a diagnostic is not trivial to implement in the general case, and no compiler actually seems to do it. Finally it's up to the programmer to make sure that such a situation does not arise in his code.

In effect, it means such things as don't use assignments to variables
in COMMON; don't assign values to any dummy arguments.

These sorts of things were probably common in the old days, but with
Fortran from F90 are far less likely to be used.

robin....@gmail.com

unread,
May 6, 2018, 11:50:36 PM5/6/18
to
Sort of defeats the purpose of functions.
Better to write pure functions, and to use an appropriate structure
as suggested in previous posts, or to assign the function reference
to a variable beforehand.

Janus

unread,
May 7, 2018, 3:59:33 AM5/7/18
to
On Sunday, May 6, 2018 at 9:41:37 PM UTC+2, Thomas Koenig wrote:
> > I have a simple code example that looks quite innocent at the
> > face of it (see my original post). The Fortran standard says it's
> > non-conforming, but apparently it does not require the compiler
> > to diagnose this.
>
> That is, indeed, the case.
>
> > Of course such a diagnostic is not trivial to
> > implement in the general case, and no compiler actually seems to
> > do it. Finally it's up to the programmer to make sure that such
> > a situation does not arise in his code.
>
> Yes.
>
> Of course, an optimization to change something like
>
> if (variable .and. function()) then
>
> to
>
> if (variable) then
> if (function()) then
> ...
>
> or even
>
> if (function() .and. variable)
>
> into
>
> if (variable) then
> if (function()) then
>
> is quite doable.

Well, I don't quite think this is a good idea, at least not the second case.

But even for the first case, doing this 'optimization' is not much help for someone wanting to write compiler-independent Fortran code.


> It is also doable to add warn about this kind
> of thing, so add this to the myriad of warnings that are already
> in place in leading compilers :-)

Giving a definitive warning like "we are optimizing away side effects here" is perhaps doable, but not so easy. Throwing a more cloudy warning like "we might possibly be optimizing away side effects here" is certainly possible.

In any case all of this is just trying to fix up a bad decision that has been taken in the Fortran standard IMHO.

I think any compiler should just refrain from doing such questionable optimizations at all, even if the Fortrand standard explicitly allows it. Luckily ifort/pgfortran/flang do that. Unfortunately gfortran does not.


> > It's clear that there are limits to this ideal conception,
> > but in the case at hand all of this mess could have been avoided,
> > if the Fortran standard would simply forbid that a function call
> > with side effects (i.e. non-pure) is optimized out. Full stop. No
> > freedom of implementation. No undiagnosable restrictions.
>
> Sure, but there are more traps than that. Fortran has very many
> rules which are supposed to aid optimization and which can be a
> trap for users.

Right. In my opinion the number of such things should be minimized, though. It is never a good idea to sacrifice everything else for the sake of a little optimization.

Cheers,
Janus

Janus

unread,
May 7, 2018, 4:18:02 AM5/7/18
to
On Monday, May 7, 2018 at 1:49:38 AM UTC+2, Ron Shepard wrote:
> On 5/6/18 1:30 PM, Janus wrote:
> > I find that quite unsatisfying, because IMHO the Fortran standard should ideally define things in a non-ambiguous way so that every compiler (that claims to be compatible with the standard) is required to produce a well-defined result, or reject the code if it is non-conforming.
>
> That is fine, except in the typical case, the compiler cannot know what
> side effects, if any, are associated with evaluation of an external
> function.

First of all, external functions are not a 'typical case' in modern Fortran, if you ask me. They should rather be a rare exception.


> So you are asking for something that is not possible.

I don't think so. It's quite simple to mark any function as side-effect-free via the PURE attribute. That even works for external functions.


> > It's clear that there are limits to this ideal conception, but in the case at hand all of this mess could have been avoided, if the Fortran standard would simply forbid that a function call with side effects (i.e. non-pure) is optimized out. Full stop. No freedom of implementation. No undiagnosable restrictions.
>
> I have explained before, a new requirement to disallow optimizations
> that have been allowed historically for 50+ years is not going to pass
> muster.

An 'optimization' in my view is an operation that improves performance in any sense, while maintaining the same results. In that sense most 'optimizations' should not be any business that the Fortran standard needs to deal with. It should provide concepts that can help a compiler with optimization, but it not need to say any word about an optimization being allowed or not, because optimization should not change any results after all.

In this sense, the thing we're talking about here is not an optimization, because it can obviously change results.


> For new programmers, an easy rule to follow is to avoid functions with
> side effects; use subroutines instead.

Ugh, strongly disagree. You make it sound like a FUNCTION is some fancy exotic concept. That may have been true in 1966, but it's certainly not in 2018.

I'd change your rule to: Mark functions without side effects as PURE, use non-pure functions only where necessary.

Cheers,
Janus

Stefano Zaghi

unread,
May 7, 2018, 4:33:25 AM5/7/18
to

> I'd change your rule to: Mark functions without side effects as PURE, use non-pure functions only where necessary.


Indeed, this is one of my pillar: write a function only when it is necessary (for me, when I want use its result into an expression, use pure subroutine otherwise), try to write it as a "pure function" and only if it is not possible (very few cases in my conditions) relax pure attribute.

Cheers.

dom...@lps.ens.fr

unread,
May 7, 2018, 4:39:26 AM5/7/18
to
Le lundi 7 mai 2018 10:18:02 UTC+2, Janus a écrit :
> On Monday, May 7, 2018 at 1:49:38 AM UTC+2, Ron Shepard wrote:
> > On 5/6/18 1:30 PM, Janus wrote:
> > > I find that quite unsatisfying, because IMHO the Fortran standard should ideally define things in a non-ambiguous way so that every compiler (that claims to be compatible with the standard) is required to produce a well-defined result, or reject the code if it is non-conforming.

My draft version of the F2018 standard contains:

10.1.7 Evaluation of operands

1 It is not necessary for a processor to evaluate all of the operands of an expression, or to evaluate entirely each operand, if the value of the expression can be determined otherwise.

NOTE 10.28
This principle is most often applicable to logical expressions, zero-sized arrays, and zero-length strings, but it applies to all expressions.

For example, in evaluating the expression
X > Y .OR. L (Z)
where X, Y, and Z are real and L is a function of type logical, the function reference L (Z) need not be
evaluated if X is greater than Y. Similarly, in the array expression
W (Z) + A
where A is of size zero and W is a function, the function reference W (Z) need not be evaluated.

2 If a statement contains a function reference in a part of an expression that need not be evaluated, all entities that would have become defined in the execution of that reference become undefined at the completion of evaluation of the expression containing the function reference.

NOTE 10.29
In the examples in NOTE 10.28, if L or W defines its argument, evaluation of the expressions under the
specified conditions causes Z to become undefined, no matter whether or not L(Z) or W(Z) is evaluated.

3 If a statement contains a function reference in a part of an expression that need not be evaluated, no invocation of that function in that part of the expression shall execute an image control statement other than CRITICAL
or END CRITICAL.

NOTE 10.30
This restriction is intended to avoid inadvertent deadlock caused by optimization.

Which "defines" things in a non-ambiguous way.

> > I have explained before, a new requirement to disallow optimizations
> > that have been allowed historically for 50+ years is not going to pass
> > muster.
>
> An 'optimization' in my view is an operation that improves performance in any sense, while maintaining the same results. In that sense most 'optimizations' should not be any business that the Fortran standard needs to deal with. It should provide concepts that can help a compiler with optimization, but it not need to say any word about an optimization being allowed or not, because optimization should not change any results after all.

For me computing

res = X > Y .OR. L (Z)

as

if (.not. (X > Y)) then
res = L(Z)
else
res = .true.
end if

is not an optimization, but a "natural" way to compute res.

Cheers,

Dominique

robin....@gmail.com

unread,
May 7, 2018, 4:51:34 AM5/7/18
to
The second form is the better of the two, for the function is not
invoked when"variable" is false.

In both cases, what is executed is clear and unequivocal.

> But even for the first case, doing this 'optimization' is not much help for someone wanting to write compiler-independent Fortran code.

BOTH forms [using two IFs] are compiler independent.

These are the best ways to write compiler-independent code.
What's more, writing pure functions avoids side effects
that can be surprising [refer to my earlier post in this thread].

> > It is also doable to add warn about this kind
> > of thing, so add this to the myriad of warnings that are already
> > in place in leading compilers :-)
>
> Giving a definitive warning like "we are optimizing away side effects here" is perhaps doable, but not so easy.

This is something that is trivial for the compiler-writer.

robin....@gmail.com

unread,
May 7, 2018, 5:06:53 AM5/7/18
to
On Monday, May 7, 2018 at 6:39:26 PM UTC+10, dom...@lps.ens.fr wrote:

> For me computing
>
> res = X > Y .OR. L (Z)
>
> as
>
> if (.not. (X > Y)) then
> res = L(Z)
> else
> res = .true.
> end if
>
> is not an optimization, but a "natural" way to compute res.

That's equivalent to a short circuit.

If you want the function to be executed always, then

res = L(Z)
res = res .or. (X > Y)

will do it.

And as for your code:-
> if (.not. (X > Y)) then
> res = L(Z)
> else
> res = .true.
> end if

better and clearer is:

if (X > Y) then
res = .true.
else
res = L(X)
end if

Wolfgang Kilian

unread,
May 7, 2018, 5:51:57 AM5/7/18
to
On 07.05.2018 10:17, Janus wrote:
> On Monday, May 7, 2018 at 1:49:38 AM UTC+2, Ron Shepard wrote:
>> On 5/6/18 1:30 PM, Janus wrote:
>>> I find that quite unsatisfying, because IMHO the Fortran standard should ideally define things in a non-ambiguous way so that every compiler (that claims to be compatible with the standard) is required to produce a well-defined result, or reject the code if it is non-conforming.
>>
>> That is fine, except in the typical case, the compiler cannot know what
>> side effects, if any, are associated with evaluation of an external
>> function.
>
> First of all, external functions are not a 'typical case' in modern Fortran, if you ask me. They should rather be a rare exception.

The typical case of external functions are C functions called via
BIND(C). Those don't always return 'void', and they can have
side-effects. They are very common in the context of larger projects.

>
>
>> So you are asking for something that is not possible.
>
> I don't think so. It's quite simple to mark any function as side-effect-free via the PURE attribute. That even works for external functions.

It is simple but leads to disaster if this is a high-level function that
you want to refactor/extend/debug later. It may be my ignorance of
efficent using of debuggers (do they really support object-oriented
Fortran?), but for me it is essential that I can always write state to
screen or file for just that purpose. When PURE was new, I welcomed it.
Today, all new functions that I write *are* actually pure, but I
stopped using the keyword. One of the best more recent additions to
Fortran was the IMPURE ELEMENTAL attribute; maintaining ELEMENTAL
procedures was a nightmare before.

I may be wrong, but I think that if code is pure Fortran and all
procedures are in modules, PURE has no positive effect at all because
the compiler can figure out the absence of side effects anyway. Vice
versa, if external procedures or non-Fortran code is involved, assuming
PURE to be appropriate is dangerous. The assumption may become wrong if
the underlying implementation changes for any reason (for instance, a
3rd party library may introduce caching for efficiency).
>
>
>>> It's clear that there are limits to this ideal conception, but in the case at hand all of this mess could have been avoided, if the Fortran standard would simply forbid that a function call with side effects (i.e. non-pure) is optimized out. Full stop. No freedom of implementation. No undiagnosable restrictions.
>>
>> I have explained before, a new requirement to disallow optimizations
>> that have been allowed historically for 50+ years is not going to pass
>> muster.
>
> An 'optimization' in my view is an operation that improves performance in any sense, while maintaining the same results. In that sense most 'optimizations' should not be any business that the Fortran standard needs to deal with. It should provide concepts that can help a compiler with optimization, but it not need to say any word about an optimization being allowed or not, because optimization should not change any results after all.

Fortran does impose requirements on user code that are not necessarily
detected. See aliasing. For the examples here, avoiding side effects
to functions is sufficient. If they can't be avoided because the
function code is non-Fortran or 3rd party, don't use the function in a
non-trivial expression.

>
> In this sense, the thing we're talking about here is not an optimization, because it can obviously change results.

It changes results only if the programmer has written non-standard code.
As mentioned elsethread, this fact has been made explicit in the
written F2018 standard: the result is undefined. The problem is that
the standard violation may not be obvious to the programmer.
>
>
>> For new programmers, an easy rule to follow is to avoid functions with
>> side effects; use subroutines instead.
>
> Ugh, strongly disagree. You make it sound like a FUNCTION is some fancy exotic concept. That may have been true in 1966, but it's certainly not in 2018.

A function with side effects may be considered exotic beginning with
standard F90 and later. Before, INTENT was not available.
>
> I'd change your rule to: Mark functions without side effects as PURE, use non-pure functions only where necessary.

I disagree, as said above.
>
> Cheers,
> Janus
>

-- Wolfgang

Dan Nagle

unread,
May 7, 2018, 11:12:46 AM5/7/18
to
Hi,

On 2018-05-07 09:51:57 +0000, Wolfgang Kilian said:

>>
>>> For new programmers, an easy rule to follow is to avoid functions with
>>> side effects; use subroutines instead.
>>
>> Ugh, strongly disagree. You make it sound like a FUNCTION is some fancy
>> exotic concept. That may have been true in 1966, but it's certainly not
>> in 2018.
>
> A function with side effects may be considered exotic beginning with
> standard F90 and later. Before, INTENT was not available.
>>
>> I'd change your rule to: Mark functions without side effects as PURE,
>> use non-pure functions only where necessary.
>
> I disagree, as said above.

A couple of items on the 202x worklist (as formal as it gets just now)
are .andthen. and .orelse. operators,
and a "verypure" function attribute (can't even read module data).

A proposal for "volatile functions" was made towards the end of work
on f03, but it was too late for inclusion, and has never been revived.
A volatile function would be referenced once per textual appearance.

--
Cheers!

Dan Nagle

Ian Harvey

unread,
May 7, 2018, 1:39:07 PM5/7/18
to
On 2018-05-08 00:42, Dan Nagle wrote:
> Hi,
>
> On 2018-05-07 09:51:57 +0000, Wolfgang Kilian said:
>
>>>
>>>> For new programmers, an easy rule to follow is to avoid functions with
>>>> side effects; use subroutines instead.
>>>
>>> Ugh, strongly disagree. You make it sound like a FUNCTION is some
>>> fancy exotic concept. That may have been true in 1966, but it's
>>> certainly not in 2018.
>>
>> A function with side effects may be considered exotic beginning with
>> standard F90 and later.  Before, INTENT was not available.
>>>
>>> I'd change your rule to: Mark functions without side effects as PURE,
>>> use non-pure functions only where necessary.
>>
>> I disagree, as said above.
>
> A couple of items on the 202x worklist (as formal as it gets just now)
> are .andthen. and .orelse. operators,

I think that describing these short circuit things as operators and
using the same sort of syntax as existing operators to be a likely
source of confusion.

Give them quite a different name and use different syntax, so that there
is no chance of conflating the required order of evaluation of operands
of these things with that of "normal" operators (which is what this
thread is about, really).

> and a "verypure" function attribute (can't even read module data).
>
> A proposal for "volatile functions" was made towards the end of work
> on f03, but it was too late for inclusion, and has never been revived.
> A volatile function would be referenced once per textual appearance.

Sounds a bit like a subroutine.

Ian Harvey

unread,
May 7, 2018, 2:11:09 PM5/7/18
to
On 2018-05-07 04:00, Janus wrote:
> On Thursday, May 3, 2018 at 5:26:41 AM UTC+2, Ian Harvey wrote:
>> My view is that the original program is non-conforming, based on
>> the sections of text mentioned by Thomas Koenig elsethread (F2008
>> 7.1.7) that give freedom to implementations around evaluation of
>> operations,
>
> These sections as such seem suggest that the code is standard
> conforming and that the compiler has some freedom of implementation.
>
>
>> and following restrictions that mean that the attempt to observe
>> the number of times that check is called by referencing the value
>> of i is not permitted.
>
> Are you talking about paragraph (2) in section 7.1.7, which says:
>
> "If a statement contains a function reference in a part of an
> expression that need not be evaluated, all entities that would have
> become defined in the execution of that reference become undefined at
> the completion of evaluation of the expression containing the
> function reference."

Yes.

> If I interpret that in your sense (which seems possible), that again
> leads me to a quite unsatisfying situation: I have a simple code
> example that looks quite innocent at the face of it (see my original
> post). The Fortran standard says it's non-conforming, but apparently
> it does not require the compiler to diagnose this. Of course such a
> diagnostic is not trivial to implement in the general case, and no
> compiler actually seems to do it. Finally it's up to the programmer
> to make sure that such a situation does not arise in his code.
>
> I bet most people writing Fortran code are actually not aware of this
> restriction (at least I wasn't), and even if they are, it's quite
> easy to miss it or forget about it. So we end up with many people
> writing non-conforming code and compilers silently swallowing it and
> producing 'arbitrary' results.

I don't know (can't know) about "most", but the restrictions in 7.1.7p2
arise naturally enough from the principle that the order of evaluation
of operands is unspecified, in such a way that there does not have to be
an order of evaluation at all.

> I find that quite unsatisfying, because IMHO the Fortran standard
> should ideally define things in a non-ambiguous way so that every
> compiler (that claims to be compatible with the standard) is required
> to produce a well-defined result, or reject the code if it is
> non-conforming.
>
> It's clear that there are limits to this ideal conception, but in the
> case at hand all of this mess could have been avoided, if the Fortran
> standard would simply forbid that a function call with side effects
> (i.e. non-pure) is optimized out. Full stop. No freedom of
> implementation. No undiagnosable restrictions.

PURE functions, using the language sense, may still have visible and
relevant side effects, as discussed upthread, perhaps you are looking
for a concept beyond that.

I disagree - I consider the freedom given to implementations in this
area an important opportunity for optimisation, and the restrictions are
easy enough to accommodate with some basic programming style rules.

The restrictions in 7.1.7p2 go beyond being able to elide calls
completely, they also allow for things such as parallel execution of
operands to the logical operators. I think it would be a shame to lose
that sort of opportunity.



David Duffy

unread,
May 7, 2018, 7:55:42 PM5/7/18
to
I am reminded of the fast-math optimizations that give incorrect answers
in many numerical applications. It was the case that one had to
specify "-fno-fast-math" when optimizing at -O2 (has that changed?). I
don't think that any language specifications were breached. That is,
optimizing out a function with side-effects seems far less worse than
silently (if you haven't fully read the manual!) degrading numerical
accuracy.

Cheers, David Duffy.

JCampbell

unread,
May 7, 2018, 10:57:15 PM5/7/18
to
Where does this come from ? This is certainly not my experience. I have structural finite element calculations that run for days and have not experienced problems of this nature.
For the rare cases where numerical underflow is a problem, I do have selective checks, which would still be required if fast-math was not used.

Can you be more specific about the problems you are encountering and how you identify the problem ?

For once I agree with Robin when he posted:

"If you want the function to be executed always, then

res = L(Z)
res = res .or. (X > Y) "

A much better solution, removes all the uncertainty.

Tim Prince

unread,
May 7, 2018, 11:53:29 PM5/7/18
to
This is veering off the topic of the thread. You can't equate normal
variations in floating point precision with make or break assumptions
about what happens when you ignore Fortran standards. However, gfortran
-O2 or -O3 don't invoke -ffast-math; normally you would specify it
explicitly if you want it, although -Ofast is the other way to get it.
Perhaps David is mixing up gfortran and ifort. ifort -O2
-standard-semantics resembles gfortran -O2 -ftree-vectorize
-fno-cx-limited-range -ffast-math. The most commonly noticed issue with
gfortran -ffast-math or ifort -fp-model fast is the slight change in
precision of simd sum reduction (usually a small improvement). There are
also questions about math libraries, with the Intel vector math library
allowing up to 4 Ulp rounding error in corner cases (mainly with
exponentiation). You have separate options to invoke (or not) those
math functions, so you can override the implications of -O levels.
If you make assumptions without reading documentation, you may come out
worse than if you ignored the issues. Admittedly, such large variations
among compilers may be annoying. Intel once said there would be a
simple option for their compilers to work like e.g. gcc -O2, but the
idea was dropped silently.
If you are depending on non-aggressive compile options to avoid problems
like depending on order of function evaluation without taking the
Fortran standard into account, or on some assumption about default
values of undefined variables, you will be bitten sooner or later.

--
Tim Prince

Janus

unread,
May 8, 2018, 5:24:26 AM5/8/18
to

> On 2018-05-08 00:42, Dan Nagle wrote:
> > A couple of items on the 202x worklist (as formal as it gets just now)
> > are .andthen. and .orelse. operators,

Thanks for the comment, Dan! I was actually hoping for something like this. It should not be too hard to implement this in gfortran (even before 2020, as a non-std extension, if necessary).


On Monday, May 7, 2018 at 7:39:07 PM UTC+2, Ian Harvey wrote:
> I think that describing these short circuit things as operators and
> using the same sort of syntax as existing operators to be a likely
> source of confusion.

Quite to the contrary, I think the introduction of extra operators would remove a lot of confusion and make things clearer.

Btw, that approach is of course exactly how C/C++ solves the problem we're discussing. They have operators like && vs & and || vs |.

I assume .andthen. would relate to .and. in the same way as && relates to &?

So, in the assignment "C = A() .and. B()" the execution of B would be guaranteed, while for "C = A() .andthen. B()" B would only be executed if necessary (namely if A evaluated to .true.). Is that the idea?

Cheers,
Janus

Ev. Drikos

unread,
May 8, 2018, 6:13:33 AM5/8/18
to
On 08/05/2018 12:24, Janus wrote:
> ...
>
> ..., I think the introduction of extra operators would remove a lot of confusion and make things clearer.
>

It seems genius, if we assume that any backward compatibility issues
with user defined operators are either negligible or somehow solvable.

BTW, in C, reserved words are sometimes added with a '_'; ie "_Noreturn"
which minimizes the possibility for collisions with user identifiers.

> Btw, that approach is of course exactly how C/C++ solves the problem we're discussing. They have operators like && vs & and || vs |.
>
> I assume .andthen. would relate to .and. in the same way as && relates to &?
>

I think that in C/C++ '|' and '&' are bitwise operators; ie 12 = 8 | 4 .

Hope this isn't out of topic.


Regards,
Ev Drikos

David Duffy

unread,
May 8, 2018, 7:16:26 AM5/8/18
to
Tim Prince <tpr...@intelretiree.com> wrote:
> On 5/7/2018 7:55 PM, David Duffy wrote:
>> I am reminded of the fast-math optimizations that give incorrect answers

> This is veering off the topic of the thread. You can't equate normal
> variations in floating point precision with make or break assumptions
> about what happens when you ignore Fortran standards. However, gfortran
> -O2 or -O3 don't invoke -ffast-math; normally you would specify it
> explicitly if you want it, although -Ofast is the other way to get it.

Well my point was just that normal variations in floating point precision can
be just as practically important as the side effects of standards-allowed
optimizations - I thought the latter behaviours acceptable, providing some
minimal level optimization can be requested eg for debugging. I have
had "-fno-fast-math" in this particular Makefile for over 10 years,
but looking back, I probably left it in after an experiment with "-Ofast".
So apologies to the gfortran maintainers.

Going right OT, as to the other remark regarding robustness of fast-math -
of course this is quite possible for many applications. Looking through
the test suite for this particular program, the only differences between
-Ofast and -O2 are: a failure in a test of the interpreter:
("4.94065646-324"+"4.94065646-324")/"4.94065646-324"
fails for -Ofast, but returns 2.0 for -O2;
differences at the ninth digit for bivariate normal integration cdfs;
262 (Ofast) v. 250 (O2) function evaluations in a likelihood maximization,
124 (Ofast) v. 109 (O2) function evaluations in a likelihood maximization
in both cases converging to the same solution;
different sequences from the particular RNG I use, leading to acceptable
differences in calculated Monte-Carlo P-values. No great difference in speed.

Cheers, David Duffy.

Battler Ushiromiya

unread,
May 8, 2018, 7:26:30 AM5/8/18
to
On Wednesday, 2 May 2018 14:59:19 UTC+1, Arjen Markus wrote:
>
> I may be mistaken, but I am sure that the reported behaviour is completely conforming: the Fortran standard does not demand shortcircuiting, but does not exclude it either.
>
> That is: if ( allocated(array) .and. array(1) > 0 ) then
> is not guaranteed to work.
>
> Similar for side effects: you do not know in which order functions are evaluated, hence you should not rely on the order.
>
> a = f(b) + f(c)
>
> might be evaluated with f(b) first and then f(c) or vice versa. The mathematical interpretation is leading.
>
> Regards,
>
> Arjen

I don't have any particularly strong opinions about logical-operator short-circuiting, but when it comes to function evaluation side-effects, I've had a (surely not original) idea after some minutes of thought.

Clearly, in a simple statement of the form:

a = f(x)

there might be some utility in f() having side-effects, I/O or so on. But once you have something like:

a = f(x) + f(y)

it becomes undefined which order these occur, which is a potential mess for either I/O, dummy variable modification (consider f(x,b) and f(y,b) with b set to INTENT(IN OUT)...), or "global" variable modification.

What would the issues be with, for lines containing more than one "side-effect inducing" call, triggering either a compiler warning, or declaring an outright language violation? (There must be *some* issue with that approach, since it, I think, isn't done, and it's a fairly "obvious" idea.)

Dan Nagle

unread,
May 8, 2018, 11:08:39 AM5/8/18
to
Hi,

On 2018-05-08 09:24:24 +0000, Janus said:

> > On 2018-05-08 00:42, Dan Nagle wrote:
>>> A couple of items on the 202x worklist (as formal as it gets just now)
>>> are .andthen. and .orelse. operators,
>
> Thanks for the comment, Dan! I was actually hoping for something like
> this. It should not be too hard to implement this in gfortran (even
> before 2020, as a non-std extension, if necessary).

.andthen. is .and., except that the second operand is evaluated only if
necessary
likewise,
.orelse. is .or., except that the second operand is evaluated only if necessary

(That is, think of .andthen. as ".and. and jump if possible".
Similarly, .orelse.)

Neither is bit-wise, both are logical.
(Fortran has iand() and ior() for bit-wise operations.)

> On Monday, May 7, 2018 at 7:39:07 PM UTC+2, Ian Harvey wrote:
>> I think that describing these short circuit things as operators and
>> using the same sort of syntax as existing operators to be a likely
>> source of confusion.
>
> Quite to the contrary, I think the introduction of extra operators
> would remove a lot of confusion and make things clearer.
>
> Btw, that approach is of course exactly how C/C++ solves the problem
> we're discussing. They have operators like && vs & and || vs |.
>
> I assume .andthen. would relate to .and. in the same way as && relates to &?
>
> So, in the assignment "C = A() .and. B()" the execution of B would be
> guaranteed, while for "C = A() .andthen. B()" B would only be executed
> if necessary (namely if A evaluated to .true.). Is that the idea?

The execution of B in the first case is not guaranteed.
It is the state we have now. (No change is proposed for existing operators.)
With .andthen., B is evaluated only when needed.

The poster-child is expressions like
( i <= n .andthen. a(i) > 0.0 )
where n is the ubound of a, of course.

--
Cheers!

Dan Nagle

Ron Shepard

unread,
May 8, 2018, 12:18:15 PM5/8/18
to
On 5/8/18 4:24 AM, Janus wrote:
> So, in the assignment "C = A() .and. B()" the execution of B would be guaranteed, while for "C = A() .andthen. B()" B would only be executed if necessary (namely if A evaluated to .true.). Is that the idea?

You are proposing to change the semantics of existing operators, and you
are suggesting it be done in such a way that disallows previously
allowed optimizations.

I'm guessing this would meet opposition from many sides.

$.02 -Ron Shepard

robin....@gmail.com

unread,
May 8, 2018, 12:26:39 PM5/8/18
to
The previous "allowed" optimisations are not what people expect,
and can lead to incorrect results.

Thomas Koenig

unread,
May 8, 2018, 1:10:40 PM5/8/18
to
Battler Ushiromiya <itspaa...@gmail.com> schrieb:
> But once you have something like:
>
> a = f(x) + f(y)
>
> it becomes undefined which order these occur, which is a potential
> mess for either I/O, dummy variable modification (consider f(x,b)
> and f(y,b) with b set to INTENT(IN OUT)...), or "global" variable
> modification.

Correct.

> What would the issues be with, for lines containing more than one
> "side-effect inducing" call, triggering either a compiler warning,
> or declaring an outright language violation? (There must be *some*
> issue with that approach, since it, I think, isn't done, and it's
> a fairly "obvious" idea.)

I am certain compilers could warn more than they currently do.

However, there are limits, especially since the code to the
function being called may not be known.

There are also some possible false positives. Consider a function
which, on the first call, builds up saved interpolation data and
uses them on the second and consecutive calls. Valid (since the
returned value, if this is implemented correctly, does not depend
on the state), but a compiler would probably flag this as erroneous.

Dominik Gronkiewicz

unread,
May 8, 2018, 1:19:24 PM5/8/18
to
I agree that optimizing out the functions, unless they are declared "pure", should be forbidden. If non-pure functions can also be carelessly optimized out by the compiler, what is the purpose of "pure" word in the first place? Once could get rid of the "pure" word altogether and assume that all functions are pure and use subroutines for tasks with side effects. A modern programming language should definetely be expressive, intuitive and prevent ambiguous situations -- here it's clearly not the case. (This reminded me of "implicit save" that I also hope will be killed in the future: https://github.com/Fortran-FOSS-Programmers/Fortran-202X-Proposals/issues/12)

Also, as someone pointed out, many external libraries written in C use functions rather than subroutines -- it would be shame to break interoperability by allowing unexpected behavior here.

Janus

unread,
May 8, 2018, 1:38:16 PM5/8/18
to
On Tuesday, May 8, 2018 at 6:18:15 PM UTC+2, Ron Shepard wrote:
> On 5/8/18 4:24 AM, Janus wrote:
> > So, in the assignment "C = A() .and. B()" the execution of B would be guaranteed, while for "C = A() .andthen. B()" B would only be executed if necessary (namely if A evaluated to .true.). Is that the idea?
>
> You are proposing to change the semantics of existing operators, and you
> are suggesting it be done in such a way that disallows previously
> allowed optimizations.

Primarily I highly welcome the addition of new operators that relieve the user from the headache of having to guess what a given compiler will do with them.

Secondly, with such an addition, there is little reason for a compiler to still apply an ('optional') short-circuiting for the traditional operators, otherwise they are no different from the new ones. So yes, why not forbid that?

Finally, even if short-circuiting for .and./.or. will not be illegalized, it's still not mandatory in current or previous standard, so compilers are still allowed to not apply it, and specially with the advent of new operators, that's what they should do. IMHO.


> I'm guessing this would meet opposition from many sides.

Phew. Why is it so hard to get rid of some old and ugly wrinkles in the Fortran language? People seem to get so used to them that they forget about their ugliness.

On a related note, here's to Fortran 2018 finally deprecating COMMON blocks!

Cheers,
Janus

dpb

unread,
May 8, 2018, 2:25:44 PM5/8/18
to
On 5/8/2018 12:38 PM, Janus wrote:
...

> Phew. Why is it so hard to get rid of some old and ugly wrinkles in the Fortran language? People seem to get so used to them that they forget about their ugliness.
...

Because there is so much old legacy code still out there, primarily.

--

Harold Stevens

unread,
May 8, 2018, 3:16:51 PM5/8/18
to
In <pcsq33$1prs$1...@gioia.aioe.org> On 2018-05-08, dpb:

[Snip...]

> Because there is so much old legacy code still out there, primarily.

... and also why so much is stll actually useful and in use ("If
it ain't broke, don't fix it").

--
Regards, Weird (Harold Stevens) * IMPORTANT EMAIL INFO FOLLOWS *
Pardon any bogus email addresses (wookie) in place for spambots.
Really, it's (wyrd) at att, dotted with net. * DO NOT SPAM IT. *
I toss GoogleGroup (http://twovoyagers.com/improve-usenet.org/).

Dominik Gronkiewicz

unread,
May 8, 2018, 8:57:39 PM5/8/18
to

> > Because there is so much old legacy code still out there, primarily.
>
> ... and also why so much is stll actually useful and in use ("If
> it ain't broke, don't fix it").

Well, I guess this discussion has been lasting for very long. As a person who has experience in multiple languages, I am also an advocate for cleaning Fortran from old ugly things, even if it breaks old codes. I think Fortran must have a major revision at some point that will drop legacy features and keep only "modern Fortran" subset or it will never catch up to modern coding practices In my opinion, it's not standard's business to care about old codes not compiling. Compiler vendors can (and probably would due to their customer demands) provide appropriate compiler switches, just as it is now. I think the problem is that in 2018 people still use common blocks, data statements, gotos and labels, so breaking these obsolete features can only benefit to the community.

Just because some feature works doesn't mean it should exist (again: implicit save being a perfect example) :)

Tim Prince

unread,
May 8, 2018, 9:48:18 PM5/8/18
to
If your application depends on gradual underflow, you must either avoid
-Ofast which sets abrupt underflow, or, to be independent of compile
options, USE ieee_arithmetic ...call IEEE_set_underflow(gradual). It's
hardly an indictment of optimization or of gfortran, which has gradual
underflow as a default with commonly used options, even -ffast-math.
Note that division with subnormal operands is slow even on recent CPUs.

--
Tim Prince

Tim Prince

unread,
May 8, 2018, 9:49:18 PM5/8/18
to
Make that ieee_set_underflow_mode(gradual).

--
Tim Prince

Ian Harvey

unread,
May 9, 2018, 7:13:27 AM5/9/18
to
On 9/05/2018 2:49 AM, Dominik Gronkiewicz wrote:
> I agree that optimizing out the functions, unless they are declared
> "pure", should be forbidden. If non-pure functions can also be
> carelessly optimized out by the compiler, what is the purpose of
> "pure" word in the first place? Once could get rid of the "pure" word
> altogether and assume that all functions are pure and use subroutines
> for tasks with side effects.

I think there is a misconception about the role of PURE.

The presence of PURE in a subprogram places restrictions on that
subprogram, restrictions that must be diagnosed by a compiler. A
subprogram that compiles in the face of those restrictions provides a
guarantee to the *programmer* that the function may be called in certain
contexts and still have a reasonably deterministic outcome.

Those certain contexts are the various language constructs that permit
execution of things in an unspecified order (which includes there being
no order) - specification parts, FORALL, DO CONCURRENT, etc. As a
further service to the *programmer*, the language requires that
procedures invoked in those constructs be PURE, and that the compiler
diagnose attempts to invoke non-pure procedures.

(The requirements on subprograms go beyond what is actually required for
a reasonably deterministic outcome, as a consequence of ensuring that
the requirements are practicable for a compiler to diagnose.)

When compiling a subprogram, compilers are already quite capable of
understanding the semantics of Fortran source to the extent needed for
fancy optimisation - the PURE keyword does not give the compiler any
more information from an optimisation perspective.

In summary - PURE is an aid for the programmer, not the compiler.

I think there is also a misconception about the role of expressions in
the language. Outside of certain pointer related contexts, expressions
are principally intended to be evaluated to get a value. Whatever else
may happen along the way is almost irrelevant from a language
perspective. Explicit freedoms given to processors to support this
include the currently discussed freedom around the order and need to
evaluate operands, and the ability to evaluate a mathematical equivalent
of the original expression. Eliding function invocations that are not
required to satisfy the principal intent of evaluating the expression is
not being "careless".

Functions can easily be used as an operand within an expression. Given
this, it makes good sense to follow some guidelines on what should go in
a function to ensure that the use of the function is going to be
consistent with the principal intent of evaluating expressions. Details
vary, exceptions exist, etc, but a very common guideline is that
functions should be written as if they are PURE, and if a "side-effect"
of a function is actually its principal effect, then that function
should be a subroutine. But these are guidelines, not language rules.

> A modern programming language should
> definetely be expressive, intuitive and prevent ambiguous situations
> -- here it's clearly not the case. (This reminded me of "implicit
> save" that I also hope will be killed in the future:
> https://github.com/Fortran-FOSS-Programmers/Fortran-202X-Proposals/issues/12)

There is no ambiguity. The code presented in the original post does not
conform to the requirements of the standard.
> Also, as someone pointed out, many external libraries written in C
> use functions rather than subroutines -- it would be shame to break
> interoperability by allowing unexpected behavior here.

If Fortran source code invokes such interoperable procedures in a manner
that complies with the rules of the Fortran language, then the behaviour
is as expected.

For interoperable functions with side effects, that simply comes down to
having the function as the only thing in an expression:

variable = c_fun(xxx)

Don't chain such function calls together in a larger expression.

David Jones

unread,
May 9, 2018, 7:54:37 AM5/9/18
to
You should look at the language "F",which was a stripped-down version
of Fortran leaving out many of the aspects of Fortran of which you
complain. It had quite a vogue for a time. It corresponded somewhat to
Fortran 90/95 I think, so that now you would have to think about what
aspects of the extrensions made to Fortran since then that you/everyone
thinks are worthwhile/essential. You would need to find support from
other users. And you would need to find someone to develop the
corresponding compiler(s).

Note this is not F* or F#.

See http://fortranwiki.org/fortran/show/F
Message has been deleted

robin....@gmail.com

unread,
May 9, 2018, 9:41:52 AM5/9/18
to
On Wednesday, May 9, 2018 at 9:54:37 PM UTC+10, David Jones wrote:

> You should look at the language "F",which was a stripped-down version
> of Fortran leaving out many of the aspects of Fortran of which you
> complain.

F omits all the obsolete and obsolescent stuff that was
error-prone in FORTRAN.

> It had quite a vogue for a time. It corresponded somewhat to
> Fortran 90/95 I think,

Yes.

Dominik Gronkiewicz

unread,
May 9, 2018, 9:44:51 AM5/9/18
to
I wrote a really nice response and then my stupid Linux crashed. Oh well.

> When compiling a subprogram, compilers are already quite capable of
> understanding the semantics of Fortran source to the extent needed for
> fancy optimisation - the PURE keyword does not give the compiler any
> more information from an optimisation perspective.
>
> In summary - PURE is an aid for the programmer, not the compiler.

I understand. Same as in C and "inline" keyword. Except that C functions can be inlined but the call logic must be preserved.

> I think there is also a misconception about the role of expressions in
> the language. Outside of certain pointer related contexts, expressions
> are principally intended to be evaluated to get a value. Whatever else
> may happen along the way is almost irrelevant from a language
> perspective. Explicit freedoms given to processors to support this
> include the currently discussed freedom around the order and need to
> evaluate operands, and the ability to evaluate a mathematical equivalent
> of the original expression. Eliding function invocations that are not
> required to satisfy the principal intent of evaluating the expression is
> not being "careless".
>
> Functions can easily be used as an operand within an expression. Given
> this, it makes good sense to follow some guidelines on what should go in
> a function to ensure that the use of the function is going to be
> consistent with the principal intent of evaluating expressions. Details
> vary, exceptions exist, etc, but a very common guideline is that
> functions should be written as if they are PURE, and if a "side-effect"
> of a function is actually its principal effect, then that function
> should be a subroutine. But these are guidelines, not language rules.

I understand this. To be honest, I always do this myself: beefy mechanics with lots of side effects in subroutines and expression evaluation in functions. And I think this freedom to optimize expressions is an amazing feature. The thing is, the presence of "pure" keyword is misleading. If all functions can be optimized out, why not force all of them to be pure, if all they are good for is expression evaluation? It will make the language clearer, more modern and will eliminate one spare keyword.

In vigorous Fortran vs C++ debate, I always advocated for Fortran that "it prevents silly mistakes". It's frustrating to see such doubtful and unclear things to be left in the language which is a workhorse mostly for people who need correct numerical results. I believe the approach of the standard is to leave "undefined behavior" for lots of things. I see it as a flaw: if the code in the OP can give undefined result, the language should never allow it to compile. If all functions were pure, this would never happen.

I think the discussion we have here is whether to leave the language open and "follow good practices" or make the language syntax force the programmer to use good practices (like in Python). I advocate for the latter.

> There is no ambiguity. The code presented in the original post does not
> conform to the requirements of the standard.

Apologies, I have re-read the whole thread, but I don't think I can understand the non-conformity. I could only see a response that standard does not say anything about it.

> For interoperable functions with side effects, that simply comes down to
> having the function as the only thing in an expression:
>
> variable = c_fun(xxx)
>
> Don't chain such function calls together in a larger expression.

Is it forbidden to use them in expressions? If they are treated just like any other functions and can be eliminated, then it should never be allowed, since C functions are effectively subroutines. My point is, that the language syntax and grammar should be natural and self-explanatory and not contain sketchy rules that can only be read deep in the standard or on message boards (look at Python as a good example).

Message has been deleted

FortranFan

unread,
May 9, 2018, 10:07:27 AM5/9/18
to
On Monday, May 7, 2018 at 3:59:33 AM UTC-4, Janus wrote:

> ..Luckily ifort/pgfortran/flang do that. Unfortunately gfortran does not.
> ..


To the contrary, it's a matter of dismay the other compilers, especially the commercial one (people pay a good dime for it), fail to optimize what's effectively "dead" code in the original post whether the 2 instructions involving the function invocation are irrelevant - they get zero marks! :-) Kudos to gfortran for achieving 50% by reducing it to a single function invocation; with appropriate compiler flags in effect (perhaps /O3 and/or -fxxx ones), hopefully gfortran can get 100% by getting rid of the both the function calls in such code!!

It seems to me OP's attempt to change or bring in some constraints in the standard to force compilers to do the desired is misplaced. The standard is rightly "neutral" on such matters.

It may be better to tap into the willingness of GNU volunteers and see if it's worth enhancing gfortran in this area and look into options of introducing warnings in the compiler (per comment 2 at the GCC bug report by OP on this) and/or other flags that help coders 'revel' in whatever deviations from good coding practices they desire, say -fno-middleend-optimize analogous to -fno-frontend-optimize!!

https://gcc.gnu.org/onlinedocs/gcc-4.9.1/gfortran/Code-Gen-Options.html#Code-Gen-Options

--- silly side-effects example ---
module m

implicit none

private

logical, save :: lfirst = .true.

public :: f

contains

function f( x ) result( r )

integer, intent(in) :: x
integer :: r

if ( lfirst ) then
r = x
lfirst = .false.
else
r = -x
end if

return

end function


end module
program p

use m, only : f

implicit none

integer, parameter :: a = 1
integer, parameter :: b = 1
integer :: x
integer :: y

x = 1
y = a*f(x) + b*f(x)
print *, "y = ", y

stop

end program
--- end silly example ---

With -ffrontend-optimize and -faggressive-function-elimination, gfortran on the above silly code gives:

y = 2

but without these options it gives

y = 0

robin....@gmail.com

unread,
May 9, 2018, 11:06:07 AM5/9/18
to
The moral of the story? is to avoid optimising.

robin....@gmail.com

unread,
May 9, 2018, 11:10:59 AM5/9/18
to
An optimiser can still stuff things up even if that is done:

variable = c_fun(xxx)
... a few lines of stuff ...
variable2 = c_fun(xxx)

may reduce the separate invocations of C_fun to a single call.

Ron Shepard

unread,
May 9, 2018, 11:12:39 AM5/9/18
to
On 5/9/18 6:13 AM, Ian Harvey wrote:
> For interoperable functions with side effects, that simply comes down to
> having the function as the only thing in an expression:
>
>   variable = c_fun(xxx)

Even this is not clear to me. For example, if VARIABLE is not referenced
after this point, and if it is a local variable and it is not a global
variable of some kind where such references might occur outside the
scope of the current subprogram, then this statement might be eliminated
as dead code. I do not know if such dead code elimination is allowed by
the standard, this is one of the gray areas in my understanding of this
situation.

The fortran standard describes when variables become defined and when
they become undefined, and this includes variables involved with
function side effects in certain situations. Having a function count the
number of times it is invoked results in such an undefined value. It is
not allowed for the programmer to reference an undefined value. However,
in practice, that value is exactly what is required. It counts,
correctly I'm presuming here, the actual number of invocations. Yes,
that count might depend on the input data to the program, and it might
depend on optimization level when various parts of the program are
compiled, but it is exactly the number that is desired at the end, even
though it is "undefined." I have wondered if it is useful to introduce a
third state for a variable, somewhere between defined (where its value
is controlled by the semantics of the program) and undefined (where its
bit pattern might be completely arbitrary), something that matches these
situations and which allows those correctly computed values to be
referenced legally.

$.02 -Ron Shepard

Dominik Gronkiewicz

unread,
May 9, 2018, 1:20:47 PM5/9/18
to
---------------------------------
#include <stdio.h>

int missiles = 100;

int send_missiles_at_enemy(int amount) {
missiles -= amount;
printf("missiles shot: %d, left: %d\n", amount, missiles);
return missiles;
}
---------------------------------
program defense

use iso_c_binding

interface
function send_missiles_at_enemy(amount) result(stat) bind(C)
import c_int
integer(c_int), value :: amount
integer(c_int) :: stat
end function
end interface

integer :: stat

stat = send_missiles_at_enemy(5)
stat = send_missiles_at_enemy(5)
stat = send_missiles_at_enemy(15)

print *, stat

end program
---------------------------------

So in this case, all calls except the last one can be optimized (since only the last changes the "value") and the spaceship will be dead?

David Jones

unread,
May 9, 2018, 1:45:14 PM5/9/18
to
Some, at least, of these problems could be ameliorated by the
re-intoduction of the ABNORMAL declaration that used to available in
the CDC compliers back in the 70's. These would be used by the
programmer for only those functions that require it and wuld appear in
the calling routine where they are needed, rather than in the called
routine, where they would be hidden.

Back then, an important use of ABNORMAL was for random number functions
where function call like " X=RAND(SEED)" within a do-loop would
otherwise be optimised away by default, resulting in a single fixed
value being put in X.

The precise meaning/effect of an ABNORMAL declaration (or an
alternative word) would need to be specified but would in principle be
"never optimise this away".

FortranFan

unread,
May 9, 2018, 1:57:04 PM5/9/18
to
On Wednesday, May 9, 2018 at 1:20:47 PM UTC-4, Dominik Gronkiewicz wrote:

> ..
>
> So in this case, all calls except the last one can be optimized (since only the last changes the "value") and the spaceship will be dead?


The action by the Fortran processor optimization will only have avoided 40% (10 out of 25 retaliation attempts) of the damage to one's soul and still place oneself infinitely worse relative to this 'divine' doctrine!! :-)) https://en.wikipedia.org/wiki/Turning_the_other_cheek

Ian Harvey

unread,
May 9, 2018, 2:45:40 PM5/9/18
to
On 10/05/2018 12:42 AM, Ron Shepard wrote:
> On 5/9/18 6:13 AM, Ian Harvey wrote:
>> For interoperable functions with side effects, that simply comes down
>> to having the function as the only thing in an expression:
>>
>>    variable = c_fun(xxx)
>
> Even this is not clear to me. For example, if VARIABLE is not referenced
> after this point, and if it is a local variable and it is not a global
> variable of some kind where such references might occur outside the
> scope of the current subprogram, then this statement might be eliminated
> as dead code. I do not know if such dead code elimination is allowed by
> the standard, this is one of the gray areas in my understanding of this
> situation.

As described, this is a different situation from the freedom given to
implementations *within an expression*. This discusses eliminating an
entire *statement*.

If the execution sequence includes that statement, then the standard
says that statement is executed. That execution requires the expression
to be evaluated.

If the Fortran processor can determine the value of c_fun(xxx) without
executing that function, then you might have an issue. But if the
interoperable procedure is provided by a companion processor, the
Fortran processor isn't going to have a fig about what it does.

Ian Harvey

unread,
May 9, 2018, 2:50:54 PM5/9/18
to
No.

But if your Fortran code was:

stat = 0 * send_missiles_at_enemy(1) &
+ MERGE(send_missiles_at_enemy(2), 0, .FALSE.)

then you are in trouble.

robin....@gmail.com

unread,
May 9, 2018, 9:13:38 PM5/9/18
to
On Thursday, May 10, 2018 at 1:12:39 AM UTC+10, Ron Shepard wrote:
> On 5/9/18 6:13 AM, Ian Harvey wrote:
> > For interoperable functions with side effects, that simply comes down to
> > having the function as the only thing in an expression:
> >
> >   variable = c_fun(xxx)
>
> Even this is not clear to me. For example, if VARIABLE is not referenced
> after this point, and if it is a local variable and it is not a global
> variable of some kind where such references might occur outside the
> scope of the current subprogram, then this statement might be eliminated
> as dead code. I do not know if such dead code elimination is allowed by
> the standard, this is one of the gray areas in my understanding of this
> situation.

Usually, the dead code message is simply a warning to the user.
In Fortran, such code cannot be deleted carte blanche, for the reason
that you state.

> The fortran standard describes when variables become defined and when
> they become undefined, and this includes variables involved with
> function side effects in certain situations. Having a function count the
> number of times it is invoked results in such an undefined value.

It does?
You should change your code.

Steve Lionel

unread,
May 10, 2018, 4:20:14 PM5/10/18
to
On 5/9/2018 7:13 AM, Ian Harvey wrote:
> When compiling a subprogram, compilers are already quite capable of
> understanding the semantics of Fortran source to the extent needed for
> fancy optimisation - the PURE keyword does not give the compiler any
> more information from an optimisation perspective.
>
> In summary - PURE is an aid for the programmer, not the compiler.

I think I understand what you are getting at, Ian, but I also think your
comments could be misconstrued.

It is certainly the case that, while compiling a PURE procedure, the
PURE keyword doesn't give the compiler additional information useful in
optimization. BUT!

For the *caller* of the PURE procedure, the keyword provides a lot of
information to the compiler, assuming an explicit interface is visible
which specifies that the procedure is pure. (The standard doesn't
require an explicit interface here unless "the procedure is used in a
context that requires it to be pure".)

Just to give one example, a compiler must assume that any module
variable or variable in any COMMON block might be modified across a
procedure call. If the PURE keyword is visible, the compiler can assume
that doesn't happen and therefore doesn't need to write back local
copies of such variables.

Even more, in the case of parallelization, a reference to a PURE
procedure allows the compiler to assume that such calls can be made in
any order without affecting the results. It also can help in inlining
decisions.

I am sure that Ian understands this, but I didn't see further discussion
of this aspect of PURE and felt it appropriate to speak up.

On the general topic of possibly eliminating calls to functions, the
discussion so far has been useful. I will point out the following text
related to expression evaluation (10.1.4p2 in 18-007):

- the evaluation of a function reference shall neither affect nor be
affected by the evaluation of any other entity within the statement;
- if a function reference causes definition or undefinition of an actual
argument of the function, that argument or any associated entities shall
not appear elsewhere in the same statement.

This further restricts side-effects of even non-PURE functions. The
standard also permits such optimizations as "code hoisting", where the
procedure call is made earlier in the program's execution than a literal
reading might imply, if the compiler can guarantee, per the rules of the
language, that the end result would be the same. All in all, functions
that have side-effects are to be avoided.

--
Steve Lionel
Retired Intel Fortran developer/support
Email: firstname at firstnamelastname dot com
Twitter: @DoctorFortran
LinkedIn: https://www.linkedin.com/in/stevelionel
Blog: http://intel.com/software/DrFortran
0 new messages