forums.silverfrost.com Forum Index forums.silverfrost.com
Welcome to the Silverfrost forums
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Feature Request: compiler option to catch aliased arguments

 
Post new topic   Reply to topic    forums.silverfrost.com Forum Index -> Suggestions
View previous topic :: View next topic  
Author Message
mecej4



Joined: 31 Oct 2006
Posts: 1885

PostPosted: Sat Apr 29, 2017 1:30 pm    Post subject: Feature Request: compiler option to catch aliased arguments Reply with quote

"Argument aliasing" is a term used (more in the C context than with Fortran) to describe situations where the same actual argument is passed by a caller for more than one dummy (formal) argument in a single CALL. The Fortran 95 standard says (http://j3-fortran.org/doc/standing/archive/007/97-007r2/pdf/97-007r2.pdf) :

Quote:
12.4.1.6 Restrictions on entities associated with dummy arguments

While an entity is associated with a dummy argument, the following restrictions hold:

(1) No action that affects the allocation status of the entity may be taken. Action that affects the value of the entity or any part of it shall be taken through the dummy argument unless...


Thus, given the heading SUBROUTINE SUB(A, B), when some parts of A or B or both A and B get changed in the subroutine, CALL SUB(C, C) is forbidden. The clauses following the "unless..." affect POINTER and TARGET variables, which are excluded from this post.

If we refer to this as the Fortran Anti Aliasing Rule (FAAR), the following is a summary of the status-quo regarding programs that violate the FAAR.

The Fortran Standard's stance: A program that violates the FAAR is "non-conforming", and the results of running such a non-conforming program are "undefined". A compiler is not required to detect such violations.

The present status:

Old mainframes and early PC Fortran compilers did only simple optimizations, and programs that violated the FAAR rule worked as intended most of the time, so the violations went undetected for years. Current compilers do more sophisticated optimizations, and those programs fail often (by producing wrong results).

Optimizing compilers such as Gfortran and IFort provide options to ratchet down the optimizations in order to let such programs work as intended, but they do not provide assistance to the programmer to locate and fix the CALLs that violate the FAAR. Early versions of G77 did, with the -falias-check option, but that option was withdrawn when GCC 2.8 stopped supporting the option.

My reasons to ask for a new feature: Programs that violate FAAR exist, although they are not widespread. It can even happen that with a medium size or large program, written today, a small change to a program can introduce a violation of FAAR where none existed before. When a violation occurs, it is very difficult and time-consuming to isolate the violation.

In the most recent instance where I faced this problem, I was using the ENLSIP least squares package (see http://forums.silverfrost.com/viewtopic.php?t=3484 ). It consists of 110 subprograms. I used FSPLIT to split apart each subprogram to a separate file. I compiled all the files with Gfortran -O0 and ran the resulting program to establish the baseline. I then compiled each of the 110 files with -O2, one at a time, followed by linking and running, until I found one file that caused the results to be different (my luck, problem present in only one subprogram). At this point I stopped and investigated whether undefined variables, subscript violations or mismatched subroutine calls could be culprits. I found a few such errors and fixed those. At this point, I remembered "aliasing", and looked for subroutine calls with duplicated actual arguments. There was only one. I tried fixing that by enclosing the second instance of the duplicated argument in parentheses. That worked with Gfortran, but failed with FTN95 because of a bug (which, Paul says, has been fixed).

Thus, my request: Please provide a /CheckAliasedArg (or similar name) compiler option to detect subprogram calls with aliased arguments. Such a check would greatly add to the impressive error-checking capabilities of FTN95.

It will probably be necessary to have both static (compile time) and dynamic (run time) components of this check. The static check could issue warnings and the dynamic check could abort with traceback.

Thanks to Paul Laidler for encouraging me to make this request.


Last edited by mecej4 on Sat Apr 29, 2017 2:03 pm; edited 6 times in total
Back to top
View user's profile Send private message
mecej4



Joined: 31 Oct 2006
Posts: 1885

PostPosted: Sat Apr 29, 2017 1:54 pm    Post subject: Reply with quote

Here is a short example of aliasing. (I had to start a new post because the previous one ran into the size limit of the forum). The program does smoothing of a vector of data using a moving average.

Code:
      PROGRAM AVG_TST
      INTEGER, PARAMETER :: N = 10
      INTEGER I
      REAL :: X, A(N+1), F = 2.0
      DO I = 1, N+1
         X = I-1
         A(I) = X*(N-X)
      END DO
      CALL AVG(A, A)
      WRITE (*, '(11F8.3)') A
     
      CONTAINS

      SUBROUTINE AVG(B, C)
      REAL, DIMENSION (10) :: B, C
      INTENT(IN) :: B
      INTENT(IN OUT) :: C
      DO I = 2, 9
         C(I) = (B(i-1)+B(i+1))/F
      END DO

      END SUBROUTINE

      END PROGRAM

FTN95 gives, with or without /opt, with or without /64:
Code:
0.000   8.000  14.500  19.250  22.125  23.063  22.031  19.016  14.008   9.000   0.000

Gfortran 6.3 with -O0, -O1 or -O2 gives the same results as FTN95, but with -O3 gives
Code:
0.000   8.000  15.000  20.000  23.000  23.500  23.000  20.000  15.000   9.000   0.000

If one is not on the lookout for aliased arguments, running through the subroutine in the debugger can be perplexing and frustrating, and one may be distracted into looking for other causes of the error (such as subscript overruns, compiler bugs, incorrectly read data, etc.)

A more intricate example of aliasing was posted in 2012 by the Sun/Oracle Fortran expert, Robert Corbett, in Usenet comp.lang.fortran: <https://groups.google.com/forum/#!topic/comp.lang.fortran/z11RW0ezojE> .


Last edited by mecej4 on Sat Apr 29, 2017 2:08 pm; edited 2 times in total
Back to top
View user's profile Send private message
PaulLaidler
Site Admin


Joined: 21 Feb 2005
Posts: 7916
Location: Salford, UK

PostPosted: Sat Apr 29, 2017 2:00 pm    Post subject: Reply with quote

Thanks for this. I can't take credit for the bug fix. I was fixed by David.
Back to top
View user's profile Send private message AIM Address
mecej4



Joined: 31 Oct 2006
Posts: 1885

PostPosted: Sat Apr 29, 2017 2:06 pm    Post subject: Reply with quote

Sorry! I have edited the post to fix this.
Back to top
View user's profile Send private message
JohnCampbell



Joined: 16 Feb 2006
Posts: 2554
Location: Sydney

PostPosted: Sun Apr 30, 2017 5:15 am    Post subject: Reply with quote

Mecej4,

Your code example of "CALL AVG(A, A)" would clearly fail.

However, I still use "Argument aliasing", especially when using memory sharing for mixed mode arrays. I appreciate this approach may offend some purists, but it works.
My "out-of-core" equation solver, which I first wrote in 1976, uses a mix of 4-byte integer index and 8-byte real value storage, all referenced by the same memory address for assembly, reduction, solution and disk transfers. Disk transfer libraries often required mixed mode arguments. Mine is based on a 4-byte integer array for all forms of transfer.

The only problem I have had with this approach is turning off the compiler errors or warnings.

I guess you are asking for a further hurdle for old code to jump.

John
Back to top
View user's profile Send private message
mecej4



Joined: 31 Oct 2006
Posts: 1885

PostPosted: Sun Apr 30, 2017 8:59 am    Post subject: Re: Reply with quote

JohnCampbell wrote:

Your code example of "CALL AVG(A, A)" would clearly fail.
I deliberately made it clear. In actual bad old code, the two arguments may have different names, related by an EQUIVALENCE statement far up in the call sequence, or could be "different" sections of a work array. Please read Robert Corbett's C.L.F. thread for an example where the possibility of failure is not so clear. Quite a few misdiagnoses there to see.

If you replace the line "A(I) = X*(N-X)" by "A(I) = X", you will see that the aliasing bug will not be manifested in the results. The subroutine, if tested during development with linearly varying data, may be declared "proven", but will fail when the input data changes (from linear to nonlinear).

Quote:
However, I still use "Argument aliasing", especially when using memory sharing for mixed mode arrays.

Aliasing is not, by itself, a source of bugs. If all the aliased arguments are INTENT(IN), whether declared so or just effectively so, there is no problem with the running of the program, but stylistic objections may be appropriate. Even when one of the arguments is written to, if loop dependencies are avoided the code may work, but only at lower levels of optimization. The ENLSIP code gave correct results with FTN95 because FTN95 does only simple optimizations.

Quote:
I guess you are asking for a further hurdle for old code to jump

No, I am asking for a stethoscope that I can use to tell when the patient is about to go belly up and take preventive action.


Last edited by mecej4 on Sun Apr 30, 2017 9:59 am; edited 1 time in total
Back to top
View user's profile Send private message
JohnCampbell



Joined: 16 Feb 2006
Posts: 2554
Location: Sydney

PostPosted: Sun Apr 30, 2017 9:30 am    Post subject: Reply with quote

Isn't the problem you are identifying similar to the requirement for private arrays in !$OMP coding ?

I found Corbett's post from Wed, 30 May 2012 20:41:16 -0700 (PDT). (I modified for IMPLICIT NONE)
Code:
 SUBROUTINE SUBR(B, C)
 real    b,c
 integer i
 DIMENSION B(10), C(5)

 DO 10 I = 1, 10
 B(I) = 2.0
 10 CONTINUE
 END

 PROGRAM COPY
 real    a
 integer i
 DIMENSION A(10)

 DO 10 I = 1,10
 A(I) = 1.0
 10 CONTINUE
 CALL SUBR(A, A(1:10:2))
 PRINT *, A
 END


If this is the example you refer to, it is interesting to see how there can be problems with a temporary array slice. Given the inefficiency of FTN95's use of array slices, I never use this coding approach and am not surprised by the problem. Hopefully my assumption is correct !!

Would ifort's approach to non-contiguous arrays solve this problem ?
real, intent(IN) :: c would fix the problem, bit it looks that C is a forgotten, unused argument that would not be expected to cause a problem when looking at SUBR


Last edited by JohnCampbell on Sun Apr 30, 2017 10:20 am; edited 1 time in total
Back to top
View user's profile Send private message
mecej4



Joined: 31 Oct 2006
Posts: 1885

PostPosted: Sun Apr 30, 2017 10:19 am    Post subject: Reply with quote

What makes Corbett's example interesting and counter-intuitive is that the subroutine does not refer to the second argument at all. Static analysis will not be sufficient to reveal a problem. Reading the source code of the subroutine (plus a cross-reference listing, if you use such listings) will not give you even a hint of any aliasing problem. Some of these strange happenings are consequences of Fortran's tradition of "separate compilation" of external subprograms.

Also to be considered is the point that in real life the subroutine may far away from the caller in the source file, and may be in a separate file. If fact, the compiler may warn you that argument B is not used in the subroutine. Yet, by its mere presence in the formal argument list, it can throw a spanner into the works.

As you noted, there are similar issues in shared memory parallel programs. When statements or machine instructions can be executed out of sequence or out of order, does the program do what the programmer intended, and does it do so without extra help in the form of compiler options or OMP directives?
Back to top
View user's profile Send private message
JohnCampbell



Joined: 16 Feb 2006
Posts: 2554
Location: Sydney

PostPosted: Sun Apr 30, 2017 10:27 am    Post subject: Reply with quote

The problem is not with SUBR at all, but in the main program, as a consequence of "CALL SUBR(A, A(1:10:2))" and knowing what the compiler will do with "A(1:10:2)". User beware ?

Although, changing SUBR to the following would fix the problem, but I have never coded a SUBR, being aware of possible array section problems.
Code:
 SUBROUTINE SUBR(B, C)
 real    b
 real, intent(IN) :: c
 integer i
 DIMENSION B(10), C(5)

 DO 10 I = 1, 10
 B(I) = 2.0
 10 CONTINUE
 END
Back to top
View user's profile Send private message
mecej4



Joined: 31 Oct 2006
Posts: 1885

PostPosted: Sun Apr 30, 2017 1:19 pm    Post subject: Re: Reply with quote

JohnCampbell wrote:
The problem is not with SUBR at all, but in the main program, as a consequence of "CALL SUBR(A, A(1:10:2))" and knowing what the compiler will do with "A(1:10:2)". User beware ?

Although, changing SUBR to the following would fix the problem, but I have never coded a SUBR, being aware of possible array section problems.[code]

Consider this real-world situation: SUBR is a user-function/subroutine that is an EXTERNAL argument to a nonlinear least squares package which is available only as a static library or a DLL; let us call it NLSMIN. SUBR has a published interface and specification, and you have written SUBR to suit. You call NLSMIN with SUBR as an argument, and NLSMIN calls SUBR with aliased array arguments, unknown to you. Things worked fine for the last five years. Now you received the latest version of your Fortran compiler with improved optimization capabilities, and the program crashes or, worse, gives slightly-off results.

I see complaints regarding such situations quite often in C.L.F. and the Intel compiler forums. Our consumer sense places suspicion first on the compiler, then our own subroutine, and only then on the NLSMIN code. But, lacking the source code of NLSMIN or daunted by the complexity of that source code, should that be available, we cannot argue that the NLSMIN code is at fault. The compiler vendor asks for a reproducer, but we cannot produce one. We check SUBR with a stand-alone driver, and the problem is not encountered. The library vendor certifies the library to work with version 2013 of the compiler, but we want to use version 2017 or 2018 Beta.

Admittedly, FAAR violations are but one among many types of errors in Fortran code, but we have tools such as FTN95 to isolate and fix many of the other types of errors. I am requesting FTN95 to be able to help us find FAAR violations. If evaluation of the effort reveals run-time alias sensing to be impractical, at least a warning could be issued when static analysis of subroutine calls and function references reveals potential aliasing errors.
Back to top
View user's profile Send private message
LitusSaxonicum



Joined: 23 Aug 2005
Posts: 2388
Location: Yateley, Hants, UK

PostPosted: Mon May 01, 2017 11:17 am    Post subject: Reply with quote

It's inevitable, I suppose, that misfits between how a subprogram is called and how it is defined will occur more frequently with separately compiled source files, and using a library seems an extreme example of that. Separate source files are at once a convenience and also a nuisance - I find an inline comment as to which source file a subprogram resides in to be a great help and one of the 'must use' features of Fortran-90 et seq. You cannot do that with a library you didn't program and compile yourself.

My own experience with external libraries is fortunately tiny: GINO-F on a VAX thirty years or so ago, GSX and Gem graphics in the early PC years. One of the great appeals of FTN-anything has been that things I wanted were part of the system, not requiring third party libraries, although I do recognise that this is because of what I want to do with it.

I must have known that it was possible to do 'Argument aliasing', but I think that I would not do it consciously for the simple reason that could easily get confused by it. For a similar reason I don't much like long argument lists, and, with the exception of WINIO@, I have never felt the need for EXTERNAL. Indeed, there is a whole section of even Fortran-77 that seems to me to be of limited utility, if for no better reason than decades down the line I will not have a clue what it was that I was trying to do 'back then'. 'Decades from now' is written more in hope than expectation, I was writing as from the perspective of decades ago!

To return to the problem in hand, one can check for SUB(C,C), but how far do you go? Checking for SUB(D,E) when both D and E are EQUIVALENCEd to C? Or in SUB, checking on the addresses of arguments?

Sometimes conformance to the standard must be the responsibility of the programmer, don't you think? It is in other areas.

Eddie
Back to top
View user's profile Send private message
mecej4



Joined: 31 Oct 2006
Posts: 1885

PostPosted: Mon May 01, 2017 1:17 pm    Post subject: Reply with quote

Well, Eddie, you may celebrate living in the First World, where contact with third party libraries is infrequent, possibly unpleasant and therefore avoidable.

In contrast, I am a heavy and regular user of large libraries such as IMSL, NAG, MKL, Lapack, MATH77, etc. So are many engineers, scientists and statisticians, who may not be expert programmers, or do not have the time, inclination or ability to write their own library routines. Where we lack in mathematical ability, we can compensate for that by liberal application of "trust but verify" and independent testing.

For such users, it is a fact of life that much good software that is available in source form is in Fortran 77 or Fortran 66, and one has to have tools for coping with the features missing in the older language.

For the project under discussion, I wrote a utility (about 120 lines, in C) to scan a fixed form Fortran source file and print out a list of all the CALLs. For example, here is the screen output:

Code:
S:\NLS\ENLSIP\CLN>\lang\scancall.exe xyz.
    7     newpnt, 16 args: call newpnt (x,n,h,l,f,m,hfunc,ffunc,mda,mdc,funcev,a,c,b,d,ier)
   10      addit,  5 args: call addit (active,inact,t,q,q) $$$
   12      addit,  5 args: call addit (active,inact,t,q,j)
   13      equal,  9 args: call equal (b,l,a,mda,n,active,t,p,p4)
   14       grad,  6 args: call grad (c,mdc,m,n,f,g)
   15     evscal,  7 args: call evscal (skale,a,mda,t,n,b,diag)


Note the $$$ at the end of the line numbered 10. That signifies that the call contains potentially aliased arguments. I then check the log file that the utility also produces, and find more details
Code:

   10      addit,  5 args: call addit (active,inact,t,q,q)
      *** addit: Args  5 and  4 (q) are the same


which I can use to investigate the source code in more detail. The on-screen listing can also be used to check if, for example, a subroutine has been called once with 43 arguments and elsewhere with 44.

Your question, "how far do you go?" is very perceptive. Static checking (number of arguments, calls with inconsistent types of arguments) is already done by current compilers, and I think that adding warnings for potential aliasing would be inexpensive. If interfaces to the callee-s are available, more useful warnings can be printed, again at little expense. Checking for overlap in the subroutine has to be dynamic, with few exceptions, and generating code to do so can be expensive for the compiler builder. On the other hand, once the problem area has been narrowed down by the static checking, it is not that difficult for the end user to add instrumentation (good old PRINT * statements) to check dummy argument addresses, etc. If the arrays are also passed with (:) and interfaces, the checking can be quite thorough.

Thanks for your interest and comments.
Back to top
View user's profile Send private message
PaulLaidler
Site Admin


Joined: 21 Feb 2005
Posts: 7916
Location: Salford, UK

PostPosted: Mon May 01, 2017 1:56 pm    Post subject: Reply with quote

I hope that this conversation does not continue for long. This recommendation is on the wish list but I have no idea at the moment whether its implementation is both feasible and within our current resources.
Back to top
View user's profile Send private message AIM Address
mecej4



Joined: 31 Oct 2006
Posts: 1885

PostPosted: Thu Mar 01, 2018 4:01 pm    Post subject: Reply with quote

The recently released NAG Fortran 6.2 compiler has a new option, -C=alias, which is intended to insert code to check when subroutine arguments break the Fortran anti-aliasing rules. The present implementation is stated to work only for scalar arguments. Thus, it will not help with the aliasing example code in this thread, which has aliasing between array arguments. However, for the example in http://forums.silverfrost.com/viewtopic.php?t=3484, where the arguments are scalar, if the first executable statement reads

CALL ADDIT(OA,OI,T,Q,Q)

one obtains (with NAG Fortran 6.2) the runtime error
Quote:
Runtime Error: xalias.f, line 21: Assignment to Q affects dummy argument K
Program terminated by fatal error
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.silverfrost.com Forum Index -> Suggestions All times are GMT + 1 Hour
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group