Path: utzoo!utgpu!utstat!jarvis.csri.toronto.edu!mailrus!nrl-cmf!ames!sgi!calvin@dinkum.SGI.COM From: calvin@dinkum.SGI.COM (Calvin H. Vu) Newsgroups: comp.lang.fortran Subject: Re: DO loops, anyone? Summary: Real Solution but long-winded. . Message-ID: <28506@sgi.SGI.COM> Date: 13 Mar 89 04:01:42 GMT References: <458@orange19.qtp.ufl.edu> Sender: daemon@sgi.SGI.COM Organization: Silicon Graphics, Inc., Mountain View, CA Lines: 360 I hope this will not turn out (for you) to be like the case of a boy's returning a book about penguin to the publisher with the explanation : "Sorry but this book tells me more about penguins than I ever wanted to know" :-). However, this issue has been discussed before without arriving at any conclusions so I would like to take this opportunity to chip in my 2c. First, to answer your question: << From bernhold@qtp.ufl.edu Sat Mar 11 07:49:54 1989 << I run the following code on my Sun 3/50 with VMS compatible f77: << << PROGRAM LOOP << IGO = 1 << DO 501 I = 1, 10 << WRITE (6,*) 'OUTER LOOP, I =',I << IF (IGO .NE. 0) GOTO 501 << DO 501 J = 1, 5 << WRITE (6,*) 'INNER LOOP, J=',J << 501 CONTINUE << STOP << END << << And I get the following output: << << OUTER LOOP, I = 1 << INNER LOOP, J= 1 << INNER LOOP, J= 2 << INNER LOOP, J= 3 << INNER LOOP, J= 4 << INNER LOOP, J= 5 << OUTER LOOP, I = 2 << OUTER LOOP, I = 3 << OUTER LOOP, I = 4 << OUTER LOOP, I = 5 << OUTER LOOP, I = 6 << OUTER LOOP, I = 7 << OUTER LOOP, I = 8 << OUTER LOOP, I = 9 << OUTER LOOP, I = 10 << << Notice that the inner DO loop is executed exactly once. << The reason the inner loop is executed only once is that there is no code generated by the compiler to determine the currently-active DO loops. Hence the jump to label 501 will execute the loop condition for the inner loop first. As you may guess, the value of J is undefined since the inner loop has never been activated (which causes the DO variable J to be initialized). The first time this jump is taken, J has the value 0 (as the undefined value on most systems but nobody can guarantee this though) and so the inner loop will get activated as if nothing is wrong (NOTE that if your inner loop were DO J=4,5 instead, you will definitely notice something funny going on since the inner loop will still be executed from 1 to 5). On exitting the loop, J will now have the value 6 and so subsequent jump will not cause the inner loop to be executed. To prove this just add one statement to your program as follows: ................ DO 501 I = 1, 10 WRITE (6,*) 'OUTER LOOP, I =',I C Add the following statement to generate a random value from 0 to 6 C for J before the jump is taken and see how randomly the innerloop C gets executed. Of course, if you want more violent behaviour you C can try any random negative values but don't be surprised if it takes C a few days to executes those random semi-infinite loops. J = IMOD(IRAND(), 6) IF (IGO .NE. 0) GOTO 501 DO 501 J = 1, 5 ........... BTW, do not feel p_ssed off if your compiler gives you complete BS. Eight out of ten compilers behave thataway. << My questions are these: << << 1) Is the code standard-conforming? I think it is, but perhaps I am << mistaken. << << 2) If the code is standard-conforming, is the execution correct? As << far as I can tell, the inner DO should never be activated! 1) It conforms with the standard in my interpretation of it. Several people, however, claim that you cannot jump to the terminal statement if that statement is shared with an inside loop. They use the general definition of the range of DO-loop to claim that since the terminal statement partly belongs to the inner loop it has to belong "exclusively" to the inner loop. Some computer systems also support this for expediency reason (I will go into that later with more data to support my theory). 2) The execution is incorrect. You are right that the inner loop should never gets activated (since it is never active according to ANSI definition of active DO loop) but wait until you see all the flames following this ... :-) << Please, no flames about the code itself. I did not write it - I just << get to port it. To make this bitchy code portable and produce the correct result across most computers that do not declaring this as illegal, just add one statement to your code outside both loops to give J a well-define value: IGO = 1 C Initialize J to the value of the inner loop's terminal value. C This will make it works on most computers that allows this C construction. However, there are a few C @#@#@% compilers (standard UNIX f77, for example) that checks for C J=6 (instead of J>5) as the terminating condition so I can't guarantee C this. J = 5 DO 501 I = 1, 10 WRITE (6,*) 'OUTER LOOP, I =',I IF (IGO .NE. 0) GOTO 501 DO 501 J = 1, 5 And now for some discussion about an old ghost, if you are not interested hit 'n' quick. It is going to be lengthy. Flame me for being long-winded. << Tim Hopkins, { trh@ukc.ac.uk << From: trh@ukc.ac.uk (T.R.Hopkins) << "More than one DO-loop may have the same terminal statement" << << Section 11.10.2 states << A DO-loop is either active or inactive. Initially inactive, a DO-loop << becomes active only when its DO statement is executed. << << Seems to me that when the GOTO is executed the inner loop is inactive. << The effect of the transfer is thus to increment the outer loop counter << (see 11.10.3 and 11.10.4) and restart the outer iteration. Hey, there is one person in the net who thinks the same way as I do. And this is the work of Steven R Weintraub (cs.utexas.edu!oakhill!devsys!steve) in the last discussion. Although he arrived at a different conclusion his posting seemed to confirm my suspicion why so many people screwed up this code construction. << Computer Compiler Result Er Msg Comment << -------------------------------- ---------------- ------ ----- ------- << AT&T 3B1 - UNIX PC Model 7300 SVS FORTRAN Yes 2 << AT&T 3B2/400 - 3B2/3B15 Computer FORTRAN 77 XLA+ 92 * << Apollo DN 3000 Aegis 9.7 65785 * << CRAY COS1.15 ? Yes 1 << Cray CFT Yes 4 << Cray CFT77 Yes 4 << Cyber 205 Vsos 237 Yes 2 << -------------------------------- ---------------- ------ ----- ------- << Cyber 855 Nos 2.5.3 82 * << Eta-10Q R0120N2 Yes 2 << Harris H-800 VOS 7.1 sauf77 82 Yes << Harris HCX-9 HCX/UX (unix) f77 82 No << Harris HCX-9 HCX/UX (unix) hf77 92 No << IBM PC/AT Professional FORTRAN Yes 2,5 << ISTLA - Toolpack Static Analyser, Version 1.2 Yes 3 << -------------------------------- ---------------- ------ ----- ------- << Prime Fortran 66 92 * << Prime Fortran 77 ? * 1 << Pyramid OSX4.0 80 * << Rolm 1666 Remora F77 (b-test) Yes 2 << Rolm 1666 Remora F77 (b-test) 80 Yes 2,6 << SUN OS 4.0 92 * << SUN OS 4.2 rel 3.5 92 * << -------------------------------- ---------------- ------ ----- ------- << VAX/VMS 4.7 92 * << *It should be pointed out that it is probably safe to assume if the result << I recieved did not mention a warning message, the compiler probably did not << generate one. << << Index to comments : << 1 Infinite loop generated << 2 Error reported from compiler - compiler produced no code. << 3 Portability anaylzer, not compiler << 4 Final answer not given in post. << 5 Compiler made by Ryan-McFarland Corporation << 6 Option to enable block jumping turned on. Ansi compatablity warning given. << << Let us look at what the proper responses should be. There are actually << three. The first is that the compiler can error out and produce no code. << Of the compilers that were reported, five did this. One of these had << a compiler option to compile through this, generating the next proper << response, 80. 80 is the result that is generated when the outer do << loop misses the K = K + 1 increment (as if a continue existed at that << line). Only one other compiler generated this result. The other correct << result is 82. This the 80 result plus two increments for K = K + 1 << from the outer loop. Only three compilers got this answer. << << Of the wrong answers recieved, they fell into two catagories : 92 and << infinitely large (or loops). 92 was the most common response to the << code, given by six compilers. This leads me to suspect I fail to see << what the code is to do, but I believe that these compilers do two << increments on the internal loop jump-out. As for the infinite answers << three computers answered this way. I generated this case knowing that << on some compilers (specifically the Apollo), DO loops are generated << technically wrong in order to make them perform extensions. From this results, I can classify them into 3 groups: 1) The Do-it-right: These are the people who do take care of active/inactive loop and produce the correct result of 82. In order to do this they have to pay a price in runtime performance and may lose (in sales) to some stupid compiler in some stupid benchmarks. However, they chose integrity over expediency over this. I would give them three cheers. It's a pity that they are the minority. << Cyber 855 Nos 2.5.3 82 * << Harris H-800 VOS 7.1 sauf77 82 Yes << Harris HCX-9 HCX/UX (unix) f77 82 No 2) The Cop-out: These guys sticks with the benchmark but have the decency not to deliberately generate random results for the same piece of code depending whatever the stack happens to contain. They cop out and give an error message for this usage. Note that ETA/Cyber belongs to both group 1 and 2. If they really think that the usage is illegal why do they allow it on another system of theirs ? I know for sure that implementing the proper solution for this would create a dependency that would severely effect vectorization of the DO loops. Therefore, my guess is that they only generate the correct code for the non-vectorizing computers only (Am I correct that Cyber 855 is not a vectorizing computer ?) and generate errors for the vectorizing computers (Cyber 205 & ETA10) to save the users from killing themselves with random results and to save them from killing themselves in the benchmarks. This approach is acceptable but I wish that the manufacturers could be more honest about it and admit their shortcoming instead of blaming the users for writing illegal code. C'mon, give me a break ! Where does it say in the ANSI standard that a DO loop termination label does not belong to its own DO loop if that label is shared by an inner loop ? 3) The Who-cares--You-asked-for-it: These guys just go merrily along and generate the usual loop control code for the shared DO loops without caring about ANSI definition of an active loop (ANSI 11.10.2: "A DO-loop is either active or inactive. Initially inactive, a DO-loop becomes active only when its DO statement is executed.") This causes the code to generate random results depending what you happened to have on the stack. Some company even goes so far to rationalize their shortcoming as to have this in their documentation: "If two or more nested DO loops share the same terminal statement, you can transfer to that statement only from within the range of the innermost loop. Any other transfer to that statement constitutes a transfer from an outer loop to an inner loop because the shared statement is part of the range of the innermost loop." The lame excuse consist of two statements that contradict one another. And maybe it's perfectly fine with them to give random results in executing exactly the same piece of code that's why they never warn the users about what REALLY HAPPENS when that "constitutes a transfer from an outer loop to an inner loop" occurs. Unfortunately, they are considered the pseudo-standard in forfran compiler so a lot of companies can cop out by simply saying "Hey, that's what the pseudo standard says and we are not doing any worse than them!". It is true that the terminal statement is shared with the inner loop but that does not cause the "constitutes blah blah ..." to happen. The statement is executed since it belongs to the currently active DO loop and the loop control processing should proceed according to ANSI rule (see 11.10.4 for details, I'm too lazy to type not that I want to quote anything out of context) : "However, if some of the DO-loops sharing the terminal statement are active, execution continues with incrementation processing, as described in 11.10.7" And ANSI 11.10.7 on incrementation processing: "(1) The DO-variable, the iteration count, and the incrementation parameter of the active DO-loop whose DO statement was most recently executed, are selected for processing." Note the words "active", and "DO statement was most recently executed". Did the DO statement for the inner loop get executed ? Then why all this constitutes-blah-blah BS ? Sorry for blowing off steam but, IMHO, a compiler, of all programs, has to be correct. It is simply frightening to think that it can deliberately be implemented/documented to bluff the users into believing that its incorrect code is correct without warning about the subsequent random results. Remind me of a bridge in Australia that collapsed while under construction (with a huge cost) due to a programming bug. Can you guess which way our missiles will be flying if some hapless programmers use this kind of construction using this kind of compiler ? BTW, the result 92 produced by many compilers is due to the initial value of 0 for the inner loop variable the first time the jump is taken and the value > terminal value for subsequent jumps. In other words, it is exactly the same thing that happened to bernhold@qtp.ufl.edu. That's all for cataloguing. Now, for my own history of the world: The shared terminal statement belongs to both loops but, in no instances, it belongs to both loops at once. In other words, it can belong to either loop at a time but not both. The rule as to which loop it belongs to is the same as the rule for loop control processing i.e. it should belong to the most current active DO loop. (It is similar to some guys sharing a baseball bat, but only one of them can go to bat at a time). This would explain the example in the ANSI standard (section 11.10.7) and gives the correct result of N=0. N=0 DO 200 I=1,10 J=1 DO 200 K=5,1 L=K 200 N=N+1 201 CONTINUE After the DO statement for the inner loop is executed, the shared terminal statement falls into its range and will not be executed (since the inner loop is K=5,1 and is not executed) even though the outside loop is active and gets executed. Since the terminal statements always belong to the most current active DO-loop any GOTO statement from inside the DO loop to its own DO label is permitted and is perfectly legal. That makes sense, doesn't it ? I would welcome any discussion on this but please remember that we are talking about a special case of DO-loops sharing the same terminal statement so please do not quote the following definition on the range of a DO loop (which I can recite backward BTW :-)) and use it as the rule-of-thumb for everything: 11.10.1 _Range_of_a_DO-Loop_. The _range_of_a_DO-Loop_ consists of all of the executable statements that appear following the DO statement that specifies the DO-Loop, up to and including the terminal statement of the DO-loop. and without quoting the statement following it: "More than one DO-loop may have the same terminal statement" and without explaining what the heck the ANSI rule talks about for loop control processing for DO-loops sharing the same terminal statements and the definition of active/inactive DO loops (the whole purpose of this active/inactive definitions is just to resolve this exact seemingly ambiguity anyway). And please do not tell me that since the two DO-loops have the same terminal statement, the statement belongs to only one of them (unless you can quote ANSI to say that the shared statement belongs exclusively to the inner loop and is not shared with the outer loop). If that's what English is I think I'd better learn Greek :-). It can surely save me a lot of headache. Calvin H. Vu Silicon Graphics Computer Systems calvin@sgi.sgi.com "Since homo sapiens have two arms and two legs by the official rule of thumb, anybody without both arms and both legs is not human". Official Definition of Human Range ------------------------------------------------------------------------- DISCLAIMER: "Nobody, even my mum, wants to have anything to do with my foul language"