Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Unititialized and SAVE

holmz
New Contributor I
697 Views

Stumbled upon an odd situation last week.

It is likely that the complier setting on the RedHat5 were using "-save" and the RedHat6 I was porting to was not.

The code looks something like this:

      PROGRAM  SunShine
      IMPLICIT NONE
!... Declarations are in here
      CALL XX(Date_n_Time, Sun_Position, SunShine)
!... File writing code is in here
      END PROGRAM SunShine

      SUBROUTINE XX(Time, Sun, SunShine)
      IMPLICIT NONE
      TYPE Position
        REAL(KIND=8) :: Lat
        REAL(KIND=8) :: Lon
        REAL(KIND=8) :: Alt
      END TYPE Position
      TYPE(Position), DIMENSION(360*181)                :: Place_as_a_Vector
!... Additional Declarations are in here...
      LOGICAL(KIND=4) DIMENSION(360,181), INTENT(  OUT) :: SunShine 
      TYPE(POSITION)                    , INTENT(IN   ) :: Sun
      REAL(KIND=8)                      , INTENT(IN   ) :: Date_n_Time
      LOGICAL(KIND=4)                                   :: Init_Me = .TRUE.

      IF(Init_Me) THEN
        K = 0
        DO I = 1, 360
          DO J = -90, 90
            K = K + 1
            Place_as_a_Vector(K)%Lat = FLOAT(J)
            Place_as_a_Vector(K)%Lon = FLOAT(I)
            Place_as_a_Vector(K)%Alt = 0.0
           ENDDO
         ENDDO
        Init_Me = .FALSE.
      ENDIF
!... Some more code here...
      RETURN
      END SUBROUTINE XX

Without either ",SAVE" on "Card 15's"  "Place_as_a_Vector" declaration line, or "Place_as_a_Vector = 0" then the variable is not saved.
Are there some strategies for catching these?

"-check uninit" is one, but I am not sure if any other -warn gives insight that can help in identifying where I should be paying attention, especially when I cam coming into a subroutine or function after the first time.

There are /QSave etc, but I probably need to make sure that the code is more bullet proof, and there are quite a few lines of code.

0 Kudos
5 Replies
IanH
Honored Contributor III
697 Views

Note that your code, as shown, has a non-sequence, non-bind(c) type defined local to an external subroutine, that is the type of of one of a non-optional argument.  There is no way that the external subroutine can be called in a conforming program.

Apart from the runtime -check options, the inspector tool might be useful for catching this sort of error.

I think having any saved state "hidden" in variables local to procedures is far from ideal.

0 Kudos
holmz
New Contributor I
697 Views

IanH wrote:
 Note that your code, as shown, has a non-sequence, non-bind(c) type defined local to an external subroutine, that is the type of of one of a non-optional argument.  There is no way that the external subroutine can be called in a conforming program.

I am not sure what you mean by "non-sequence, non-bind(c) type defined local to an external subroutine", and I am poking in the code from memory as we do not have decent internet connectivity at work. I am passing in a time and a position and getting out an array of logicals.

Sticking in the ,SAVE fixed the problem, but being able to find others without beard-stroking each routine would be useful. The code now looks like:

      TYPE(Position), DIMENSION(360*181)  , SAVE          :: Place_as_a_Vector

 

IanH wrote:
 Apart from the runtime -check options, the inspector tool might be useful for catching this sort of error. 

I think I agree, but I do not believe that we have inspector installed,.. only the ifort and icc. Or maybe I need to figure out how to get to inspector on Linux??

 

IanH wrote:
 I think having any saved state "hidden" in variables local to procedures is far from ideal. 

The calling routine or main routine does not need insight into the array called "Places_as_an_Array", and I only want to initialise this once. So the variable to save the initialisation also seems to be not required to be resident in the caller.

What would you call ideal?
Having this array in a module? And then the module present in the caller and the callee?
I may need to balance "ideal" with also being somewhat being readable by F77 users. In this case a module would work, but I am upon to ideas.

0 Kudos
mecej4
Honored Contributor III
697 Views

If the code was written to work with an older (especially mainframe) compiler, pinching one's nose and using the -save option is the simplest solution. Microsoft FPS and Compaq/Digital CVF adopted the convention of SAVE for all variables by default.

However, you can look for blocks of code with a tell-tale sign, such as the variable Init_Me of type Logical and assigned the initial value .TRUE., followed by a block of code surrounded by IF (Init_Me) THEN...END IF. All local variables to which values are assigned in the loop probably need the attribute SAVE.

Secondly, look for local variables that are used in an expression with no previous statements that define them. These may need SAVE as well, but it can be difficult to decide when the code contains lots of jumps and conditional statements.

0 Kudos
IanH
Honored Contributor III
697 Views

holmz wrote:
I am not sure what you mean by "non-sequence, non-bind(c) type defined local to an external subroutine", and I am poking in the code from memory as we do not have decent internet connectivity at work.

I suspect your actual code is different then... my point was that the type Position is defined locally in the subroutine (its external nature is a red herring).  That type doesn't have BIND(C) or SEQUENCE, so that definition is unique - even if the same fragment of Fortran source for the type appeared elsewhere it would define a different type.  One of the dummy arguments of that subroutine is of that local type, and that dummy argument is not optional, hence any call to the subroutine must include an actual argument of that type.  But because the type definition is strictly local, it is impossible to declare or create an object that could be such an actual argument, hence it is impossible to call the procedure in a conforming manner.

It is a question of programming style, rather than a hard guideline, but I very much prefer procedures to be procedural.  With limited exceptions I do not use SAVE'd variables in procedures at all.  If there is state to be passed from procedure invocation to procedure invocation, then I carry that state across in a dummy argument.  This simplifies the situation when code inevitably evolves to require multiple instances of that state to be extant at one time, or for the state to be reset or similar.  Note though, that I am talking about my stance for new code, rather than limited maintenance of old code.

0 Kudos
holmz
New Contributor I
697 Views

mecej4 wrote:
However, you can look for blocks of code with a tell-tale sign, such as the variable Init_Me of type Logical and assigned the initial value .TRUE., followed by a block of code surrounded by IF (Init_Me) THEN...END IF. All local variables to which values are assigned in the loop probably need the attribute SAVE.

Well I cannot blame the "Init_Me" and "_as_a_vector" on anyone else... ;) Those clearly are my doing.

It does not appear that I can make use of compiler checks and warning to catch these.

I think I will also have to look at BIND(C)...

 

IanH wrote:

Quote:

holmz wrote:
I am not sure what you mean by "non-sequence, non-bind(c) type defined local to an external subroutine", and I am poking in the code from memory as we do not have decent internet connectivity at work.

 

I suspect your actual code is different then... my point was that the type Position is defined locally in the subroutine (its external nature is a red herring).  That type doesn't have BIND(C) or SEQUENCE, so that definition is unique - even if the same fragment of Fortran source for the type appeared elsewhere it would define a different type.  One of the dummy arguments of that subroutine is of that local type, and that dummy argument is not optional, hence any call to the subroutine must include an actual argument of that type.  But because the type definition is strictly local, it is impossible to declare or create an object that could be such an actual argument, hence it is impossible to call the procedure in a conforming manner.

The card #3 was meant to indicate that the same structure/type was in the main routine.
And that record is actually in a module that the main and the subroutine use. But poking it in by hand from recollection is always fraught with memory/brain leaks.
 

IanH wrote:
It is a question of programming style, rather than a hard guideline, but I very much prefer procedures to be procedural.  With limited exceptions I do not use SAVE'd variables in procedures at all.  If there is state to be passed from procedure invocation to procedure invocation, then I carry that state across in a dummy argument.  This simplifies the situation when code inevitably evolves to require multiple instances of that state to be extant at one time, or for the state to be reset or similar.  Note though, that I am talking about my stance for new code, rather than limited maintenance of old code.

Ah... Yes...
Something to keep in mind... This code is not threaded or using any implicit parallelism (yet), but I will ponder the implication of this.

 

0 Kudos
Reply