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

removing old goto's

514 views
Skip to first unread message

BlindAnagram

unread,
Mar 9, 2021, 1:36:34 PM3/9/21
to
I am converting some very old code and I came across a code sequence
that left a simple result, so simple that I don't trust my result.

I would hence appreciate input from Fortran aficionados on what result
they obtain in fully simplifying this code fragment:

-----------------------------------------------------
if (r == 0 .or. i == 1 .or. x == 0.0) go to 30
v1 = l
if (m <= (n - 2) .and. c <= 5.0) go to 30
if (m <= (n - 4) .and. c <= 10.0) go to 30
if (m <= (n - 6) .and. c <= 15.0) go to 30
if (m <= (n - 8) .and. c <= 20.0) go to 30
call f()
30
-----------------------------------------------------

Any help much appreciated.

gah4

unread,
Mar 9, 2021, 3:39:34 PM3/9/21
to
On Tuesday, March 9, 2021 at 10:36:34 AM UTC-8, BlindAnagram wrote:

(snip)

> if (r == 0 .or. i == 1 .or. x == 0.0) go to 30
> v1 = l
> if (m <= (n - 2) .and. c <= 5.0) go to 30
> if (m <= (n - 4) .and. c <= 10.0) go to 30
> if (m <= (n - 6) .and. c <= 15.0) go to 30
> if (m <= (n - 8) .and. c <= 20.0) go to 30
> call f()
> 30

If you mean simplifying so much that some of the IF statements
are not needed, I don't think that is right. There are values that
satisfy each IF statement, but don't satisfy those above or below.

Richard Weed

unread,
Mar 9, 2021, 4:40:37 PM3/9/21
to
EXIT is your friend (provided your compiler is F2008 compliant)

GO30: if (r /= 0 .and. i /= 1 .and. x /= 0.0) then
v1 = l
if (m <= (n - 2) .and. c <= 5.0) EXIT GO30
if (m <= (n - 4) .and. c <= 10.0) EXIT GO30
if (m <= (n - 6) .and. c <= 15.0) EXIT GO30
if (m <= (n - 8) .and. c <= 20.0) EXIT GO30
call f()
end if GO30

BlindAnagram

unread,
Mar 9, 2021, 6:43:32 PM3/9/21
to
Not quite. It is possible to reverse the logic on each line so that, for
example,

if (m <= (n - 2) .and. c <= 5.0) go to 30

becomes:

if (m > (n - 2) .or. c > 5) then
....

This eliminates all the goto's and, if I am right, some of the four
similar lines above (in their new form) can be combined to achieve some
further simplification.

BlindAnagram

unread,
Mar 9, 2021, 6:44:48 PM3/9/21
to
Thanks, I'll look at that possibility


Lynn McGuire

unread,
Mar 9, 2021, 6:56:37 PM3/9/21
to
Very dangerous to modify working code without a complete analysis.

Lynn

BlindAnagram

unread,
Mar 9, 2021, 7:09:09 PM3/9/21
to
Agreed.

MAGNIN Vincent

unread,
Mar 10, 2021, 2:49:52 AM3/10/21
to
Negating conditions with .not. could be less error prone than inversing
evrything (although less clear):

if (.not.(m <= (n - 2) .and. c <= 5.0)) then

Arjen Markus

unread,
Mar 10, 2021, 4:46:46 AM3/10/21
to
How about:

if (r /= 0 .and. i /= 1 .and. x /= 0.0) then
v1 = l
skip = &
(m <= (n - 2) .and. c <= 5.0) .or. &
(m <= (n - 4) .and. c <= 10.0) .or. &
(m <= (n - 6) .and. c <= 15.0) .or. &
(m <= (n - 8) .and. c <= 20.0)
if ( .not. skip ) then
call f()
endif
endif

Regards,

Arjen

Thomas Koenig

unread,
Mar 10, 2021, 5:42:56 AM3/10/21
to
BlindAnagram <blinda...@nowhere.org> schrieb:

> I would hence appreciate input from Fortran aficionados on what result
> they obtain in fully simplifying this code fragment:
>
> -----------------------------------------------------
> if (r == 0 .or. i == 1 .or. x == 0.0) go to 30
> v1 = l
> if (m <= (n - 2) .and. c <= 5.0) go to 30
> if (m <= (n - 4) .and. c <= 10.0) go to 30
> if (m <= (n - 6) .and. c <= 15.0) go to 30
> if (m <= (n - 8) .and. c <= 20.0) go to 30
> call f()
> 30
> -----------------------------------------------------

Here's what toolpack makes of it, after making the code
stnippet compilable and replacing all the <= with .LE. etc:

$ /usr/local/toolpack1.2/tool_scripts/stf tst2.f
[ISTLY Normal Termination]
PROGRAM MAIN
INTEGER R,I,L,V1,M,N
REAL X

IF (R.NE.0 .AND. I.NE.1 .AND. X.NE.0.0) THEN
V1 = L
IF (M.GT. (N-2) .OR. C.GT.5.0) THEN
IF (M.GT. (N-4) .OR. C.GT.10.0) THEN
IF (M.GT. (N-6) .OR. C.GT.15.0) THEN
IF (M.GT. (N-8) .OR. C.GT.20.0) CALL F
ENDIF

ENDIF

ENDIF

ENDIF

END
[ISTST Normal Termination]

A straightforward translation, negating the conditions of the
GOTOs along the way.

BlindAnagram

unread,
Mar 10, 2021, 11:26:33 AM3/10/21
to
Thanks, that is what I used, but i wondered if it could be simplified
further.

BlindAnagram

unread,
Mar 10, 2021, 12:19:40 PM3/10/21
to
Thanks to you (and others) for providing answers. I believe that would
work. What I ended up with is:

if (r /= 0 .and. i /= 1 .and. x1 /= 0.0) then
v1 = 1
if (m > n - 2 .or. (m >= n - 4 .and. cm > 5) .or. &
(m >= n - 8 .and. cm > 10) .or. cm > 20) then
call f()
endif
endif



Robin Vowels

unread,
Mar 10, 2021, 10:20:53 PM3/10/21
to
.
If it works, don't fix it.

James Van Buskirk

unread,
Mar 11, 2021, 1:04:28 AM3/11/21
to
"Thomas Koenig" wrote in message
news:s2a7re$ase$1...@newsreader4.netcologne.de...
I like to eliminate as many IF statements as possible because I seem to
have so much trouble getting the logic right:

PROGRAM MAIN
INTEGER R,I,L,V1,M,N
REAL X

IF (R.NE.0 .AND. I.NE.1 .AND. X.NE.0.0) THEN
V1 = L
IF(.NOT.ANY(m <= n-[2,4,6,8] .AND. c <= [5.0,10.0,15.0,20.0])) CALL
F
ENDIF

END

We could use de Morgan to get rid of the .NOT. there but that would
involve rethinking the logic when it already fits in 7:72.

James Van Buskirk

unread,
Mar 11, 2021, 2:59:35 AM3/11/21
to
"Thomas Koenig" wrote in message
news:s2a7re$ase$1...@newsreader4.netcologne.de...

> PROGRAM MAIN
> INTEGER R,I,L,V1,M,N
> REAL X

> IF (R.NE.0 .AND. I.NE.1 .AND. X.NE.0.0) THEN
> V1 = L
> IF (M.GT. (N-2) .OR. C.GT.5.0) THEN
> IF (M.GT. (N-4) .OR. C.GT.10.0) THEN
> IF (M.GT. (N-6) .OR. C.GT.15.0) THEN
> IF (M.GT. (N-8) .OR. C.GT.20.0) CALL F
> ENDIF

> ENDIF

> ENDIF

> ENDIF

> END

I replied to this but it seems to have gotten lost. The gist of my comment
was

PROGRAM MAIN
INTEGER R,I,L,V1,M,N
REAL X

IF (R.NE.0 .AND. I.NE.1 .AND. X.NE.0.0) THEN
V1 = L

JCampbell

unread,
Mar 11, 2021, 3:40:44 AM3/11/21
to
I don't like what toolpack makes of it, with nested IF or the complexity of understanding what ANY does.

My only preference would have been to change to the following, (when I was younger!)
if (n-m >= 2 .and. c <= 5.0) go to 30
if (n-m >= 4 .and. c <= 10.0) go to 30
if (n-m >= 6 .and. c <= 15.0) go to 30
if (n-m >= 8 .and. c <= 20.0) go to 30

as I find it more understandable and possibly easier to debug,

But if it is "old code" that works, I do agree with Robin.
What does removing goto achieve ?

BlindAnagram

unread,
Mar 11, 2021, 4:46:57 AM3/11/21
to
Generally I would agree.

But the code sets limits on where switches are between different
algorithms for doing a function computation and we now have more
knowledge of what the limits should be.

And, although I am of an age, where 'goto's were the only thing
available, I now much prefer to understand code in a more modern style
before I embark on necessary changes.

BlindAnagram

unread,
Mar 11, 2021, 4:52:49 AM3/11/21
to
Apologies for the disappearance of your post as I suspect I caused this
by making an erroneous post which I quickly deleted.

Your suggestion is a very interesting one that I will certainly have a
look at since it might well be a significantly easier one to work with.

Thank you.

BlindAnagram

unread,
Mar 11, 2021, 5:03:40 AM3/11/21
to
On 11/03/2021 08:40, JCampbell wrote:
> On Thursday, March 11, 2021 at 6:59:35 PM UTC+11, James Van Buskirk wrote:
>> "Thomas Koenig" wrote in message
>> news:s2a7re$ase$1...@newsreader4.netcologne.de...

[snip]
> I don't like what toolpack makes of it, with nested IF or the complexity of understanding what ANY does.
>
> My only preference would have been to change to the following, (when I was younger!)
> if (n-m >= 2 .and. c <= 5.0) go to 30
> if (n-m >= 4 .and. c <= 10.0) go to 30
> if (n-m >= 6 .and. c <= 15.0) go to 30
> if (n-m >= 8 .and. c <= 20.0) go to 30
>
> as I find it more understandable and possibly easier to debug,

Yes, I agree with your preferences and I ended up with:

if (r /= 0 .and. i /= 1 .and. x /= 0.0) then
v1 = 1
if(n - m < 2 .or. (n - m < 4 .and. c > 5.0) &
.or. (n - m < 6 .and. c > 10.0) &
.or. (n - m < 8 .and. c > 15.0) &
.or. c > 20.0) then
call f()
end if
end if

although others have suggested interesting alternatives that I will
consider.

My thanks to all who have taken the time to offer help with this.

Cyrmag

unread,
Mar 11, 2021, 6:45:17 AM3/11/21
to
On 3/9/2021 12:36 PM, BlindAnagram wrote:
The four "if (m.." statements fit into a nice pattern, so you could
write a DO loop:

do j = 1, 4
if(m <= n - 2*j .and. c <= 5.0*j) go to 30
end do
call f()
30 ...

Ev. Drikos

unread,
Mar 11, 2021, 7:51:09 AM3/11/21
to
On 09/03/2021 20:36, BlindAnagram wrote:
> I am converting some very old code and I came across a code sequence
> that left a simple result, so simple that I don't trust my result.

I wouldn't be surprised if the condition you found is as simple as the
2nd conversion below. But this is more a guess than a credible result,
as I haven't made any exercises with De Morgan Law since 1995.

> I would hence appreciate input from Fortran aficionados on what result
> they obtain in fully simplifying this code fragment:
>
> -----------------------------------------------------
>    if (r == 0 .or. i == 1 .or. x == 0.0) go to 30
>    v1 = l
>    if (m <= (n - 2) .and. c <=  5.0) go to 30
>    if (m <= (n - 4) .and. c <= 10.0) go to 30
>    if (m <= (n - 6) .and. c <= 15.0) go to 30
>    if (m <= (n - 8) .and. c <= 20.0) go to 30
>        call f()
> 30
> -----------------------------------------------------
>
> Any help much appreciated.

@OP

I would start with the code below and add any nested conditions
required.

Ev. Drikos

----------------------------------------------------------
if (r /= 0 .and. i /= 1 .and. x /= 0.0) then
v1 = l
if (m > (n - 8) .or. c > 5.0) then
! @OP : add nested conditions as needed

CyrMag

unread,
Mar 12, 2021, 4:51:15 AM3/12/21
to
On Thursday, March 11, 2021 at 5:45:17 AM UTC-6, CyrMag wrote:
I wrote:
> The four "if (m.." statements fit into a nice pattern, so you could
> write a DO loop:
>
> do j = 1, 4
> if(m <= n - 2*j .and. c <= 5.0*j) go to 30
> end do
> call f()
> 30 ...

If you wish to avoid the GO TO altogether, you can write:

do j = 1,4
if (m > 2*j .or. c > 5.0*j)then
if (j .eq. 4) call f()
endif
end do

-- CyrMag

abrsvc

unread,
Mar 12, 2021, 7:17:22 AM3/12/21
to
I'm a little late to the party here, but having been programming in Fortran and others for 40+ years, I would vote for writing code in such a manner that it is easily understood by anyone reading it. If using a GOTO statement makes sense and makes following the code easy, use it. I recall having this discussion many years ago when "structured programming" was the in thing and GOTO statements were discouraged. Ultimately, there will be a goto function executed as a JUMP instruction to skip over code. These days, compilers are very good at making code efficient. Why spend time making code difficult, let the compiler take care of efficiencies in the generated instruction stream. Make the code simple and easy to understand and follow. I wonder how much programming time is wasted making the code complex in an attempt to save a few lines or to avoid certain constructs.

Dan

Richard Weed

unread,
Mar 12, 2021, 9:57:26 AM3/12/21
to
On Friday, March 12, 2021 at 6:17:22 AM UTC-6, abrsvc wrote:
> I'm a little late to the party here, but having been programming in Fortran and others for 40+ years, I would vote for writing code in such a manner that it is easily understood by anyone reading it. If using a GOTO statement makes sense and makes following the code easy, use it. I recall having this discussion many years ago when "structured programming" was the in thing and GOTO statements were discouraged. Ultimately, there will be a goto function executed as a JUMP instruction to skip over code. These days, compilers are very good at making code efficient. Why spend time making code difficult, let the compiler take care of efficiencies in the generated instruction stream. Make the code simple and easy to understand and follow. I wonder how much programming time is wasted making the code complex in an attempt to save a few lines or to avoid certain constructs.
>
> Dan

I've done a lot of restructureing of code over the years and have in the past gone to great
lengths to remove every GOTO I could. I still try to remove the ones that branch back or
loop up and replace them with unindexed DO's but I've found that trying to refactor complicated
or nested IFs with several compound logical clauses can lead to some unintended consequences
and are best left as close as possible to the original code as you can. Unless something has
changed in recent compilers, Fortran does not short circuit compound if clauses. The compilers (optimizers) are free to process them in any order. Here is an actual example I ran into several
years ago.

If (A .NE. 0.0 .AND. C .LT. B/A) Then

The compiler was evaluating the second clause (C .LT. B/A) with A == 0.0 before the first (A.NE.0.0) and generating a divide by zero error. Even if you don't encounter problems like this
with valid (restructured) code you run the risk of the optimizer changing the order of execution which in turn effects the accumulation of error in floating point calculations. It's not uncommon to see (usually very small but still noticable) changes in the final results. If your goal is for your refactored codes result match the original code bit for bit then you need to rethink if
your restructuring is worth it.

Re. the discussion/debate/flame wars that erupt whenever the subject of GOTO is brought up I
remember someone in an online discussion quoting Linus Torvald who supposedly said something like " Its not that GOTO's are inherently evil, its how they are implemented in some languages"
I think thats true of Fortran. I presume its been proposed and shot down many time in the past but I still don't undertstand why in this day and age Fortran statement labels are still limited to
numerical values only. Making them fully alphanumeric would open the door to making the use of
GOTO a lot more palatable for the few instances were than can still be justified. Its still hard to beat GOTO for getting out of a section of code on an error condition and jumping down to a segment of code reserved for error handling. So instead of

If (error_occurs) GOTO 1000

We could have

If (error_occurs) GO TO error_handler_1

I also thing this would be useful with trying to refactor old Format statements
so instead of

Write(10,1000) A(1:5)
.
Several thousand lines of code later
.
1000 Format(5F13.7)
We could have have
Write(10,FIVE_FLOATS) A(1:5)

FIVE_FLOATS: Format(5(F13.7)

(And yes I know you can accompish something similar by putting the formats in character strings)

Again, just one guys opinion but I think small changes like this can go a long way to
encouraging people to do at least a minimal amount of refactoring that would make old
code a lot more readable for younger eyes.




Thomas Koenig

unread,
Mar 12, 2021, 10:43:00 AM3/12/21
to
Richard Weed <rwee...@gmail.com> schrieb:

> Re. the discussion/debate/flame wars that erupt whenever the
> subject of GOTO is brought up I remember someone in an online
> discussion quoting Linus Torvald who supposedly said something
> like " Its not that GOTO's are inherently evil, its how they are
> implemented in some languages"

I have a different opinion there.

It's not the goto that is problematic. It's when you read a label,
and it is not clear under which conditions the goto occurs, what
the values of the variables are etc.

[...]

> 1000 Format(5F13.7)
> We could have have
> Write(10,FIVE_FLOATS) A(1:5)
>
> FIVE_FLOATS: Format(5(F13.7)
>
> (And yes I know you can accompish something similar by putting the formats in character strings)

It is also possible to use

character(len=*), parameter :: five_floats = '(5(F13.7))'

with no disadvantate (I think).

Robin Vowels

unread,
Mar 12, 2021, 11:46:47 AM3/12/21
to
On Saturday, March 13, 2021 at 1:57:26 AM UTC+11, Richard Weed wrote:
> On Friday, March 12, 2021 at 6:17:22 AM UTC-6, abrsvc wrote:
> > I'm a little late to the party here, but having been programming in Fortran and others for 40+ years, I would vote for writing code in such a manner that it is easily understood by anyone reading it. If using a GOTO statement makes sense and makes following the code easy, use it. I recall having this discussion many years ago when "structured programming" was the in thing and GOTO statements were discouraged. Ultimately, there will be a goto function executed as a JUMP instruction to skip over code. These days, compilers are very good at making code efficient. Why spend time making code difficult, let the compiler take care of efficiencies in the generated instruction stream. Make the code simple and easy to understand and follow. I wonder how much programming time is wasted making the code complex in an attempt to save a few lines or to avoid certain constructs.
> >
> > Dan
> I've done a lot of restructureing of code over the years and have in the past gone to great
> lengths to remove every GOTO I could. I still try to remove the ones that branch back or
> loop up and replace them with unindexed DO's but I've found that trying to refactor complicated
> or nested IFs with several compound logical clauses can lead to some unintended consequences
> and are best left as close as possible to the original code as you can. Unless something has
> changed in recent compilers, Fortran does not short circuit compound if clauses. The compilers (optimizers) are free to process them in any order. Here is an actual example I ran into several
> years ago.
>
> If (A .NE. 0.0 .AND. C .LT. B/A) Then
>
> The compiler was evaluating the second clause (C .LT. B/A) with A == 0.0
> before the first (A.NE.0.0) and generating a divide by zero error.
.
I think that you will find that the executable code evaluated A .NE. 0.0 first,
then evaluated C .LT. B/A next.
The executable code would then evaluate the .AND. operation.
.
The code is incorrectly written.
It must be written
IF (A .NE. 0.0) THEN
IF (C .LT. B/A) THEN

Ron Shepard

unread,
Mar 12, 2021, 11:47:00 AM3/12/21
to
This is not equivalent code.

For readability, I like the above test sequence, but I usually prefer to
introduce a logical variable and loop exit rather than use the goto.
Something like:

doit = .true.
test_loop: do j = 1, 4
if ( m <= n - 2*j .and. c <= 5*j) then
doit = .false.
exit test_loop
endif
enddo
if ( doit ) call f()

The equivalent ANY() expression which was posted earlier is also easy to
understand for a human, and it also, as a practical matter, short
circuits when appropriate, but the compiler must build the temporary
arrays to do the tests, so that seems inelegant. However, if the test
expressions were already stored as arrays, that would be my preference.

$.02 -Ron Shepard

Cyrmag

unread,
Mar 12, 2021, 12:49:11 PM3/12/21
to
On 3/12/2021 3:51 AM, CyrMag wrote:
> On Thursday, March 11, 2021 at 5:45:17 AM UTC-6, CyrMag wrote:
> I wrote:
>> The four "if (m.." statements fit into a nice pattern, so you could
>> write a DO loop:
>>
>> do j = 1, 4
>> if(m <= n - 2*j .and. c <= 5.0*j) go to 30
>> end do
>> call f()
>> 30 ...
>
> If you wish to avoid the GO TO altogether, you can write:
>
> do j = 1,4
> if (m > 2*j .or. c > 5.0*j)then
> ...
Sorry, this last line should have been

if ( m > n - 2*j .or. c > 5.0*j) then

-- CyrMag

Richard Weed

unread,
Mar 12, 2021, 4:00:05 PM3/12/21
to
@Robin Vowels

This was about 20 years ago in a DoE lab code that I didn't write.
I know enough not to make that mistake and would write it the way
you show with the A.NE.0.0 clause in a separate if. It did however
generate a divide by zero and I had to convince the DoE physics major
who wrote it that his logic was wrong. However, he kept insisting that
Fortran would evaluate the A.NE.0.0 first, something to the best of
my knowledge (going on 50 years of programming Fortran) that Fortran
has never done.

gah4

unread,
Mar 12, 2021, 7:10:19 PM3/12/21
to
On Friday, March 12, 2021 at 1:00:05 PM UTC-8, Richard Weed wrote:

(snip)

> This was about 20 years ago in a DoE lab code that I didn't write.
> I know enough not to make that mistake and would write it the way
> you show with the A.NE.0.0 clause in a separate if. It did however
> generate a divide by zero and I had to convince the DoE physics major
> who wrote it that his logic was wrong. However, he kept insisting that
> Fortran would evaluate the A.NE.0.0 first, something to the best of
> my knowledge (going on 50 years of programming Fortran) that Fortran
> has never done.

As far as I know, and pretty far back, Fortran allows but does not require
short circuit evaluation. On the other hand, C and Java require it, so
programmers can depend on it.

Also, as far as I know, the evaluation of array expressions don't short
circuit, though I suppose it is possible. I don't see it as much now,
but for some years after array expressions were added to Fortran,
some people liked to figure out how to use them, even if the result
was more complicated and harder to follow. But often they require
evaluating the whole expression, where a DO loop with an early exit
would not.

gah4

unread,
Mar 12, 2021, 9:01:24 PM3/12/21
to
On Friday, March 12, 2021 at 8:47:00 AM UTC-8, Ron Shepard wrote:

(snip)

> For readability, I like the above test sequence, but I usually prefer to
> introduce a logical variable and loop exit rather than use the goto.
> Something like:
>
> doit = .true.
> test_loop: do j = 1, 4
> if ( m <= n - 2*j .and. c <= 5*j) then
> doit = .false.
> exit test_loop
> endif
> enddo
> if ( doit ) call f()

> The equivalent ANY() expression which was posted earlier is also easy to
> understand for a human, and it also, as a practical matter, short
> circuits when appropriate, but the compiler must build the temporary
> arrays to do the tests, so that seems inelegant. However, if the test
> expressions were already stored as arrays, that would be my preference.

I am not sure that compilers are very good at optimizing array expressions
to short circuit the evaluation. That is much less obvious than short circuit
on an IF statement, or other logical expression.

As to the arrays:

>>> IF(.NOT.ANY(m <= n-[2,4,6,8] .AND. c <= [5.0,10.0,15.0,20.0])) CALL

There need to be somewhere the two array constants.
Most obviously, then, it generates two temporary logical arrays,
.AND.s them, and applies ANY. It would be interesting to know
about compilers that can optimize that one.

Reminds me, there is an example from an actual IBM manual:

DO 11 1=1,10
DO 12 J=1,10
9 IF (B(I).LT.O) GO TO 11
12 C(J)=SQRT(B(I)
11 CONTINUE

It seems that the optimizer in the IBM Fortran H compiler is good enough
to move the SQRT out of the J loop, but leaves the IF in the loop.

I suspect, then, that it could get some other IF constructs wrong, too.

JCampbell

unread,
Mar 12, 2021, 10:18:31 PM3/12/21
to
It is all the fault of "the DoE physics major" ! (Was Lynn's a Physics Phd ?)
Those physicists have been a common cause of problems, especially since they did not have the more general education of us engineers !!
Changing to another compiler can often show up our lazy past.

James Van Buskirk

unread,
Mar 13, 2021, 2:32:47 AM3/13/21
to
"gah4" wrote in message
news:9b08a70c-7bd9-4959...@googlegroups.com...
Suppose we have a data segment:

i2468: dd 2, 4, 6, 8
f5101520: dd 5.0, 10.0, 15.0, 20.0

Then eax = n, ecx = m, xmm2(0:31) = c

sub eax, ecx
movd xmm0, eax
vpbroadcastd xmm0, xmm0
movdqa xmm1, [i2468]
pcmpgta xmm1, xmm2
vpbroadcastd xmm2, xmm2
movaps xmm3, [f5101520]
cmpleps xmm2, xmm3
vptest xmm1, xmm2
jnc skip
call f
skip:

Hopefully the interested reader can check my untested code above.
The problem seems to be set up for SSE to perform all the arithmetic,
comparisons, and ANY in parallel so the cost of doing all four is the
same as the cost of doing any one.
Thus the array syntax may be both easier to read (especially if
on two lines so that corresponding array elements were lined up)
and more efficient.

Ev. Drikos

unread,
Mar 13, 2021, 5:18:01 AM3/13/21
to
On 11/03/2021 14:51, Ev. Drikos wrote:
> On 09/03/2021 20:36, BlindAnagram wrote:
>> I am converting some very old code and I came across a code sequence
>> that left a simple result, so simple that I don't trust my result.
>
> I wouldn't be surprised if the condition you found is as simple as the
> 2nd conversion below. But this is more a guess than a credible result,
> as I haven't made any exercises with De Morgan Law since 1995.
>

Ok, I can't see an easy simplification thereafter. A straightforward
conversion could also be ie:

if (r /= 0 .and. i /= 1 .and. x /= 0.0) then
v1 = l
if (m <= (n - 2) .and. c <= 5.0) then
else if (m <= (n - 4) .and. c <= 10.0) then
else if (m <= (n - 6) .and. c <= 15.0) then
else if (m <= (n - 8) .and. c <= 20.0) then
else
call f()
end if
end if

Still, I can't see any obvious gain compared to the OP example!


James Van Buskirk

unread,
Mar 13, 2021, 7:00:16 AM3/13/21
to
"James Van Buskirk" wrote in message news:s2hpql$pd9$1...@dont-email.me...

> Suppose we have a data segment:

> i2468: dd 2, 4, 6, 8
> f5101520: dd 5.0, 10.0, 15.0, 20.0

> Then eax = n, ecx = m, xmm2(0:31) = c

> sub eax, ecx
> movd xmm0, eax
> vpbroadcastd xmm0, xmm0
> movdqa xmm1, [i2468]
> pcmpgta xmm1, xmm2
> vpbroadcastd xmm2, xmm2
> movaps xmm3, [f5101520]
> cmpleps xmm2, xmm3
> vptest xmm1, xmm2
> jnc skip
> call f
> skip:

> Hopefully the interested reader can check my untested code above.

Testing revealed a couple of typos. Therefore:

D:\gfortran\clf\condition>type test.asm
format MS64 COFF

section '.text' code readable executable align 16
extrn f
public testme
testme:
sub edx, ecx
movd xmm0, edx
vpbroadcastd xmm0, xmm0
movdqa xmm1, [i2468]
pcmpgtd xmm1, xmm0
vpbroadcastd xmm2, xmm2
movaps xmm3, [f5101520]
cmpleps xmm2, xmm3
vptest xmm1, xmm2
jnc skip
sub rsp, 40
call f
add rsp, 40
skip:
ret

section 'data' data readable align 16
i2468: dd 2, 4, 6, 8
f5101520: dd 5.0, 10.0, 15.0, 20.0

D:\gfortran\clf\condition>fasm test.asm
flat assembler version 1.71.49 (1048576 kilobytes memory)
3 passes, 298 bytes.

D:\gfortran\clf\condition>type condition.f90
module stuff
use ISO_C_BINDING
implicit none
interface
subroutine testme(m,n,c) bind(C,name='testme')
import
implicit none
integer(C_INT), value :: m
integer(C_INT), value :: n
real(C_FLOAT), value :: c
end subroutine testme
end interface
contains
subroutine f() bind(C,name='f')
write(*,'(a)') 'Subroutine f was called'
end subroutine f
end module stuff

program condition
use stuff
implicit none
integer, parameter :: marray(0:1) = [16,17]
real, parameter :: carray(0:1) = [10.0,nearest(10.0,1.0)]
real r, x, v1, c
integer i, n, m
integer j

n = 20
r = 1.0
i = 2
x = 1.0
write(*,'(a)') 'Original'
do j = 0, 3
m = marray(mod(j,2))
c = carray(j/2)
write(*,'(*(g0))') 'm = ',m,', n = ',n,', c = ',c
if(r == 0 .OR. i == 1 .OR. x == 0.0) go to 30
v1 = 1
if(m <= (n-2) .AND. c <= 5.0) go to 30
if(m <= (n-4) .AND. c <= 10.0) go to 30
if(m <= (n-6) .AND. c <= 15.0) go to 30
if(m <= (n-7) .AND. c <= 20.0) go to 30
call f
30 continue
end do
write(*,'(a)') 'Converted Fortran'
do j = 0, 3
m = marray(mod(j,2))
c = carray(j/2)
write(*,'(*(g0))') 'm = ',m,', n = ',n,', c = ',c
if(.NOT.(r == 0 .OR. i == 1 .OR. x == 0.0)) then
v1 = 1
if(.NOT.any(m <= n-[2,4,6,8] .AND. c <= [5.0,10.0,15.0,20.0])) call
f
end if
end do
write(*,'(a)') 'Converted FASM'
do j = 0, 3
m = marray(mod(j,2))
c = carray(j/2)
write(*,'(*(g0))') 'm = ',m,', n = ',n,', c = ',c
if(.NOT.(r == 0 .OR. i == 1 .OR. x == 0.0)) then
v1 = 1
call testme(m,n,c)
end if
end do
end program condition

D:\gfortran\clf\condition>gfortran condition.f90 test.obj -ocondition

D:\gfortran\clf\condition>condition
Original
m = 16, n = 20, c = 10.0000000
m = 17, n = 20, c = 10.0000000
Subroutine f was called
m = 16, n = 20, c = 10.0000010
Subroutine f was called
m = 17, n = 20, c = 10.0000010
Subroutine f was called
Converted Fortran
m = 16, n = 20, c = 10.0000000
m = 17, n = 20, c = 10.0000000
Subroutine f was called
m = 16, n = 20, c = 10.0000010
Subroutine f was called
m = 17, n = 20, c = 10.0000010
Subroutine f was called
Converted FASM
m = 16, n = 20, c = 10.0000000
m = 17, n = 20, c = 10.0000000
Subroutine f was called
m = 16, n = 20, c = 10.0000010
Subroutine f was called
m = 17, n = 20, c = 10.0000010
Subroutine f was called

D:\gfortran\clf\condition>

gah4

unread,
Mar 13, 2021, 7:34:18 AM3/13/21
to
On Friday, March 12, 2021 at 11:32:47 PM UTC-8, James Van Buskirk wrote:

(snip, I wrote)

> > I am not sure that compilers are very good at optimizing array expressions
> > to short circuit the evaluation. That is much less obvious than short
> > circuit
> > on an IF statement, or other logical expression.
>
> > As to the arrays:
>
> > >>> IF(.NOT.ANY(m <= n-[2,4,6,8] .AND. c <= [5.0,10.0,15.0,20.0])) CALL
>
> > There need to be somewhere the two array constants.
> > Most obviously, then, it generates two temporary logical arrays,
> > .AND.s them, and applies ANY. It would be interesting to know
> > about compilers that can optimize that one.
>
(snip)

> Suppose we have a data segment:
>
> i2468: dd 2, 4, 6, 8
> f5101520: dd 5.0, 10.0, 15.0, 20.0
>
> Then eax = n, ecx = m, xmm2(0:31) = c
>
> sub eax, ecx
> movd xmm0, eax
> vpbroadcastd xmm0, xmm0
> movdqa xmm1, [i2468]
> pcmpgta xmm1, xmm2
> vpbroadcastd xmm2, xmm2
> movaps xmm3, [f5101520]
> cmpleps xmm2, xmm3
> vptest xmm1, xmm2
> jnc skip
> call f
> skip:
>
> Hopefully the interested reader can check my untested code above.
> The problem seems to be set up for SSE to perform all the arithmetic,
> comparisons, and ANY in parallel so the cost of doing all four is the
> same as the cost of doing any one.
> Thus the array syntax may be both easier to read (especially if
> on two lines so that corresponding array elements were lined up)
> and more efficient.

Pretty neat.

I think I like putting the constants into arrays, but it could still be done
with a DO loop.

Related questions used to come up with arrays much larger,
but okay with SSE you can do it four or so times faster.

It seems I have been writing about these for 20 years or so.
Maybe compilers are better by now. Maybe they can figure
this one out. There used to be some using the MASK argument,
where I suspect the usual way is to evaluate the whole thing,
where a loop could exit early.

But with four, maybe it doesn't matter.

Simon Geard

unread,
Mar 13, 2021, 1:15:21 PM3/13/21
to
On 09/03/2021 18:36, BlindAnagram wrote:
> I am converting some very old code and I came across a code sequence
> that left a simple result, so simple that I don't trust my result.
>
> I would hence appreciate input from Fortran aficionados on what result
> they obtain in fully simplifying this code fragment:
>
> -----------------------------------------------------
>    if (r == 0 .or. i == 1 .or. x == 0.0) go to 30
>    v1 = l
>    if (m <= (n - 2) .and. c <=  5.0) go to 30
>    if (m <= (n - 4) .and. c <= 10.0) go to 30
>    if (m <= (n - 6) .and. c <= 15.0) go to 30
>    if (m <= (n - 8) .and. c <= 20.0) go to 30
>        call f()
> 30
> -----------------------------------------------------
>
> Any help much appreciated.

In order to provide an alternative to the excellent suggestions so far
how about moving the decision logic into a separate subroutine to avoid
program clutter:

program alt_goto
implicit none

integer :: m, n
real :: c

abstract interface
subroutine sr()
end subroutine sr
end interface

procedure(sr), pointer :: action

m = 3
n = 6
c = 7.0

action => choose_action(m, n, c)

call action()

stop

contains
subroutine f()
write(*,'(a)') 'f called'
end subroutine f

subroutine do_nothing()
end subroutine do_nothing

function choose_action(m, n, c) result(r)
integer, intent(in) :: m, n
real, intent(in) :: c
procedure(sr), pointer :: r
integer :: k

r => f
if (c <= 20.0 .and. 2 < n-m) then
do k=2,8,2
if (c <= 2.5*k .and. k < n-m) then
r => do_nothing
exit
end if
end do
end if
return

end function choose_action

end program alt_goto


gfortran -o gotos gotos.f90 -Wall -Wextra
./gotos
f called

This is quite possibly the least efficient way of doing this but
sometimes program clarity is more important. I would be interested to
know if it's reasonable to expect a compiler to optimise away a call to
do_nothing if that's selected.

Robin Vowels

unread,
Mar 13, 2021, 11:15:33 PM3/13/21
to
.
The original was absolutely clear and unequivocal.
There's no need at all to change it (except to use the modern form of operators).
As I said upthread, if it works, don't fix it.
It doesn't need pointers, it doesn't need functions etc etc.

Simon Geard

unread,
Mar 14, 2021, 7:03:56 AM3/14/21
to
On 14/03/2021 04:15, Robin Vowels wrote:
> On Sunday, March 14, 2021 at 5:15:21 AM UTC+11, Simon Geard wrote:
>> On 09/03/2021 18:36, BlindAnagram wrote:
>>> I am converting some very old code and I came across a code sequence
>>> that left a simple result, so simple that I don't trust my result.
>>>
>>> I would hence appreciate input from Fortran aficionados on what result
>>> they obtain in fully simplifying this code fragment:
>>>
>>> -----------------------------------------------------
>>> if (r == 0 .or. i == 1 .or. x == 0.0) go to 30
>>> v1 = l
>>> if (m <= (n - 2) .and. c <= 5.0) go to 30
>>> if (m <= (n - 4) .and. c <= 10.0) go to 30
>>> if (m <= (n - 6) .and. c <= 15.0) go to 30
>>> if (m <= (n - 8) .and. c <= 20.0) go to 30
>>> call f()
>>> 30
>>> -----------------------------------------------------
>>>
>>> Any help much appreciated.
>> In order to provide an alternative to the excellent suggestions so far
>> how about moving the decision logic into a separate subroutine to avoid
>> program clutter:
>>
-- snip --
>>
>> This is quite possibly the least efficient way of doing this but
>> sometimes program clarity is more important. I would be interested to
>> know if it's reasonable to expect a compiler to optimise away a call to
>> do_nothing if that's selected.
> .
> The original was absolutely clear and unequivocal.
> There's no need at all to change it (except to use the modern form of operators).
> As I said upthread, if it works, don't fix it.
> It doesn't need pointers, it doesn't need functions etc etc.
>

I'm not suggesting this as a real alternative or even an improvement,
just a slightly different way of looking at the problem. However I don't
agree that the the original is absolutely clear and unequivocal since to
me (n-m) >= 2 is definitely better than m <= (n-2). There is also
potentially a problem with the equality part of the test on 'c' although
that can't be known without seeing the context.

As for it not needing pointers, functions etc - I completely agree with
you, but that doesn't mean they shouldn't be used.

The oft repeated mantra here of "if it works don't fix it" is something
I profoundly disagree with. It does have merit and should be a
consideration but that's its limit. It is far too easy to use it as an
excuse to not do anything or create no-go areas in a large code base. It
is also the enemy of innovation, education and maintenance.

Thomas Koenig

unread,
Mar 14, 2021, 8:53:47 AM3/14/21
to
Simon Geard <si...@whiteowl.co.uk> schrieb:
> However I don't
> agree that the the original is absolutely clear and unequivocal since to
> me (n-m) >= 2 is definitely better than m <= (n-2).

They are also not equivalent in all cases.

If n = -huge(n), then n-2 will overflow.

jfh

unread,
Mar 14, 2021, 4:54:38 PM3/14/21
to
So may n-m. If n = -huge(n) then m <= (n-2) overflows for any value of m, but (n-m) >=2 overflows if m>0. And the compilers I can access don't warn about integer overflows.

JCampbell

unread,
Mar 14, 2021, 7:31:25 PM3/14/21
to
The following is similar to the original code, removes all goto's and may provide a structure for more tests
if (r /= 0 .and. i /= 1 .and. x /= 0.0) then
v1 = l
do k = 1,4
if (n-m >= 2*k .and. c <= 5.0*k) exit
end do
if ( k > 4 ) call f
end if

But why bother ?

FortranFan

unread,
Mar 14, 2021, 8:52:27 PM3/14/21
to
On Thursday, March 11, 2021 at 4:46:57 AM UTC-5, BlindAnagram wrote:

> ..
> Generally I would agree.
>
> But the code sets limits on where switches are between different
> algorithms for doing a function computation and we now have more
> knowledge of what the limits should be.
>
> And, although I am of an age, where 'goto's were the only thing
> available, I now much prefer to understand code in a more modern style
> before I embark on necessary changes.

@BlindAnagram ,

Re: "I now much prefer to understand code in a more modern style before I embark on necessary changes," as you note, modern style involves structured programming.

But that's not all: modern approach also involves overall analysis, data structures, and algorithms.

Here you've presented about 8 lines of code with 5 GOTO instructions.

Readers have no insight into the rest of the code around this. Under the circumstances, any feedback here will be akin to "premature optimization", the root of "evil" per Knuth.

Your comment, "the code sets limits on where switches are between different algorithms for doing a function computation and we now have more knowledge of what the limits should be" suggests you've done some analysis. You should also consider suitable data structure(s) toward the switches and limits - it need not be complicated, something simple involving logical/integer arrays and/or derived types may suffice - the key aspect will be to employ mnemonic object(s) that can be self-documenting. With suitable data structures, you will need the code constructs become straightforward as well, that a lot of convoluted IF statements melt away in the process.

Robin Vowels

unread,
Mar 15, 2021, 2:04:03 AM3/15/21
to
.
It may have removed the GOTO's, but had I come across a piece of
code like that in a program, I'd have to puzzle over what on earth is he
(the author) doing?
.
It shouldn't take effort to work out what a piece of code is doing.
It's complicated by the loop.
.
On the other hand, it takes only a glance with the original code
to see what it does. It does not require an analysis to do that.

BlindAnagram

unread,
Mar 15, 2021, 9:04:19 AM3/15/21
to
On 15/03/2021 00:52, FortranFan wrote:
> On Thursday, March 11, 2021 at 4:46:57 AM UTC-5, BlindAnagram wrote:
>
>> ..
>> Generally I would agree.
>>
>> But the code sets limits on where switches are between different
>> algorithms for doing a function computation and we now have more
>> knowledge of what the limits should be.
>>
>> And, although I am of an age, where 'goto's were the only thing
>> available, I now much prefer to understand code in a more modern style
>> before I embark on necessary changes.
>
> @BlindAnagram ,
>
> Re: "I now much prefer to understand code in a more modern style before I embark on necessary changes," as you note, modern style involves structured programming.
>
> But that's not all: modern approach also involves overall analysis, data structures, and algorithms.

The piece of code I presented, with the only request that I would like
to know how others would write it in Fortran without goto's, did not
require a wider context (as many examples given here show).

[snip]

> Your comment, "the code sets limits on where switches are between different algorithms for doing a function computation and we now have more knowledge of what the limits should be" suggests you've done some analysis.

Of the fragment I presented, yes. It would have been impossible (for me
at least) to transform it into a couple of 'if then' clauses without
doing an analysis of the boolean logic involved in the transformation.

spectrum

unread,
Mar 15, 2021, 5:52:23 PM3/15/21
to
@here

To me, the biggest downside of GOTO is that, it is not immediately clear
what the coder tries to do by reading the line containing the GOTO statement.
For example, suppose I read this line:

if (<some-condition>) goto 1000

then I need to read the line 1000 to understand the intent of the coder
(unless some local rule is present (*1)).

On the other hand, it is often easier (to me ) to understand the intent of the coder
if it is written with other constructs (w/o goto).

(*1) In F77, I often used something like

if (<some-condition>) goto 9000

which means some error handling (according "my" local rule), then
it is immediately "clear" to me that it is error handling. But, I think the meaning of
that statement is not so clear when someone reads this statement w/o that information...

-----

In the OP's case, I might use some boolean flag in my code, e.g.

need_xxx = .false.
if (<some-condition>) need_xxx = .true.
...
if (need_xxx) call calc_xxx()

-----

RE "don't touch the existing code if it works", I think
it depends how the code is used by people (or the precise meaning
of "touch"). If the code is to be used as a black box, there may be
no meaning to update the coding style. However, if the code is to be
updated contiguously by people, it should be useful to make it
more readable / understandable (so helping reduce
the overhead to modify it).

# Btw, I've recently bought this book
https://martinfowler.com/books/refactoring.html
because the amazon rep was good, but not sure if it is interesting...XD
I guess the topic in this thread is also related to the general topic of "refactoring"
https://en.wikipedia.org/wiki/Code_refactoring

Gary Scott

unread,
Mar 15, 2021, 8:02:42 PM3/15/21
to
On 3/15/2021 4:52 PM, spectrum wrote:
> @here
>
> To me, the biggest downside of GOTO is that, it is not immediately clear
> what the coder tries to do by reading the line containing the GOTO statement.
> For example, suppose I read this line:
>
> if (<some-condition>) goto 1000
>
> then I need to read the line 1000 to understand the intent of the coder
> (unless some local rule is present (*1)).

Well, there's always value in proper/thorough comments. I would have
likely had an inline comment after the goto to explain whats going on
explicitly.

There's way too much dependence on trying to read code in absence of
proper documentation. While fortran is more conversational/less cryptic
than some programming languages, good commentary can achieve miracles!

Walter Spector

unread,
Jun 10, 2021, 1:12:52 PM6/10/21
to
Coming into this rather late - I would suggest using the (F2008) block construct:

b1: block
if(r == 0 .OR. i == 1 .OR. x == 0.0) exit b1
v1 = 1
if(m <= (n-2) .AND. c <= 5.0) exit b1
if(m <= (n-4) .AND. c <= 10.0) exit b1
if(m <= (n-6) .AND. c <= 15.0) exit b1
if(m <= (n-7) .AND. c <= 20.0) exit b1
call f ()
end block b1

On the surface, it may appear pretty much identical to the original 'go to' code - just adding 'syntactic sugar'. However by using block construct, it is clear that the scope of the branching is limited in a structured fashion. E.g., in the original code, a 'go to 30' could appear in any number of other places in the code besides the above five places. With the block construct, no such possibilities exist.

Lynn McGuire

unread,
Jun 10, 2021, 5:55:52 PM6/10/21
to
I like code comments. Sometimes they are even correct !

Lynn
0 new messages