Free plusFORT Licence for Fortran Discourse users

Well, since it was not a bug the reformatting does make it easier to spot the error by eye though.
Some old code splits long lines in the middle of variable names and other nasty things and something like spag that automatically refactors that eliminates one of the tedious things to look for when moving fixed format code to free format, which otherwise is often just changing old style comments to ! and changing continued lines which is often (not always) straight-forward.

@apple3feet, can I report a conversion bug? I tried to convert some old code from netlib and noticed that a well-known fragment of code (determining the machine epsilon) was converted incorrectly. Here is the original code:

C
C Compute the machine precision EPS.
C
      EPS = 1.D0
    1 EPS = EPS/2.D0
        EPSP1 = STORE(EPS+1.D0)
        IF (EPSP1 .GT. 1.D0) GO TO 1
      EPS = 2.D0*EPS

and this is the conversion result:

!
! Compute the machine precision EPS.
!
      eps = 1.D0
      do
         eps = eps/2.D0
         epsp1 = store(eps+1.D0)
         if ( epsp1<=1.D0 ) then
            eps = 2.D0*eps
!
! Compute abscissae X uniformly distributed in [0,1],
!   ordinates Y from Y(I) = f(X(I)), for f(x) = x**2,
!   true derivative values YPTRUE from f'(x) = 2*x,
!   zero tension factors SIGMA, bounds B for SIGBI, and
!   uniform weights W for SMCRV.
!
            h = 1.D0/dble(nm1)
            do i = 1 , n
               xi = dble(i-1)*h
               ...

The loop for epsilon does not stop correctly and becomes merged with loops that follow.

(FYI this is alg716.f in netlib.org)

Thanks for this. Are you sure it’s wrong? I’d need more code to be sure, but I think it’s OK. It looks as though SPAG has converted the loop back to label 1 into a DO…ENDDO and reversed the logic on the IF to create an exit from the loop.

EPS = 1.D0
do
EPS = EPS/2.D0
EPSP1 = STORE(EPS+1.D0)
IF (EPSP1 .LE. 1.D0) then
GO TO 2
endif
enddo
2 EPS = 2.D0*EPS
more code
3 and more

It has then moved the code at label 2 inside the loop - replacing GOTO 2. The moved code should be followed by GOTO 3 or EXIT.

John

I noticed the loop was much longer than I expected. I will check it again. Thanks for the feedback.

You’re absolutely right. The result is a very long do-loop, but the body of it is in an IF-block that only executed if the value of 1+eps equals 1. Not a transformation a human being would think of. Or at least not this one.

Indeed, I found the transformation inhuman. SPAG turned a three-line loop constructed with IF and GOTO into a DO loop that spanned more than 300 lines. Most of those lines were sucked in from the portion of the program after the IF-GOTO loop and before the next STOP statement in the program. Such undesirable transformations could be avoided if SPAG were to compute a figure of merit to decide whether to use a certain transformation, with a penalty component that discourages the creation of long blocks of code constructs.

And given that an EXIT is required either way, it would’ve been better to keep the new loop short. The problem arises because there are 2 restructuring operations which, individually, may make sense, but when combined do not. I think the order of the two changes made the difference. Cases like this are unusual, and I’m hesitant to get too clever with this stuff, as it’s hard to foresee all the consequences.

John

Yes, I quite understand the predicament! It seems an example of the “chaotic” character of source code :slight_smile: . Could an interactive mode (*) help out here? One where alternatives suggested or the possibility of a user identifying pieces of code to which a particular transformation is to be applied. Just thinking out loud.

(*) I do not necessarily mean a sophisticated GUI, but rather an annotation system or the like.

It’s a good idea, and it rang a bell. In fact it already exists, but I had forgotten - it’s even in the manual. If you put “!-ANCHOR” before the first line of a block of code, SPAG will not relocate the block, and in this case does what is required.

John

C
C Compute the machine precision EPS.
C
      EPS = 1.D0
    1 EPS = EPS/2.D0
        EPSP1 = STORE(EPS+1.D0)
        IF (EPSP1 .GT. 1.D0) GO TO 1
C-ANCHOR
      EPS = 2.D0*EPS
2 Likes

Wonderful! I will give that a try :slight_smile:

Hm, I do not see any effect, even worse: the directive remains in the processed code. Here is the original code with the anchor:

C
C Compute the machine precision EPS.
C
      EPS = 1.D0
    1 EPS = EPS/2.D0
        EPSP1 = STORE(EPS+1.D0)
        IF (EPSP1 .GT. 1.D0) GO TO 1
      EPS = 2.D0*EPS
C
C Compute abscissae X uniformly distributed in [0,1],
C   ordinates Y from Y(I) = f(X(I)), for f(x) = x**2,
C   true derivative values YPTRUE from f'(x) = 2*x,
C   zero tension factors SIGMA, bounds B for SIGBI, and
C   uniform weights W for SMCRV.
C
!-ANCHOR
  111 H = 1.D0/DBLE(NM1)
      DO 2 I = 1,N
        XI = DBLE(I-1)*H
        X(I) = XI
        ...

and here is what SPAG produces:

!
! Compute the machine precision EPS.
!
      eps = 1.D0
      do
         eps = eps/2.D0
         epsp1 = store(eps+1.D0)
         if ( epsp1<=1.D0 ) then
            eps = 2.D0*eps
!
! Compute abscissae X uniformly distributed in [0,1],
!   ordinates Y from Y(I) = f(X(I)), for f(x) = x**2,
!   true derivative values YPTRUE from f'(x) = 2*x,
!   zero tension factors SIGMA, bounds B for SIGBI, and
!   uniform weights W for SMCRV.
!
!-ANCHOR
            h = 1.D0/dble(nm1)
            do i = 1 , n
               xi = dble(i-1)*h
               x(i) = xi
               ...

I checked the manual - the anchored statement must be executable and have a label, so I guess this is correct.

Oh, solved the riddle: you also need to use this artificial label (111). Otherwise it is ignored.

So, the annotated code now is:

C
C Compute the machine precision EPS.
C
      EPS = 1.D0
    1 EPS = EPS/2.D0
        EPSP1 = STORE(EPS+1.D0)
        IF (EPSP1 .GT. 1.D0) GO TO 1
      EPS = 2.D0*EPS
      GOTO 111
C
C Compute abscissae X uniformly distributed in [0,1],
C   ordinates Y from Y(I) = f(X(I)), for f(x) = x**2,
C   true derivative values YPTRUE from f'(x) = 2*x,
C   zero tension factors SIGMA, bounds B for SIGBI, and
C   uniform weights W for SMCRV.
C
!-ANCHOR
  111 H = 1.D0/DBLE(NM1)
      DO 2 I = 1,N
        XI = DBLE(I-1)*H
        X(I) = XI
        ...

Hi

Thanks for your patience. The block being moved starts with

EPS = 2.0D0*EPS

and the anchor needs to be before that statement. The manual does say there should be a label, but in my tests, it was not needed - I will review that. The key is that SPAG looks at the first statement of the block which is a candidate to be relocated.

I get

eps = 1.D0
DO
eps = eps/2.D0
epsp1 = STORE(eps+1.D0)
IF ( epsp1<=1.D0 ) EXIT
ENDDO
!-ANCHOR
eps = 2.D0eps
!
! Compute abscissae X uniformly distributed in [0,1],
! ordinates Y from Y(I) = f(X(I)), for f(x) = x**2,
! true derivative values YPTRUE from f’(x) = 2
x,
! zero tension factors SIGMA, bounds B for SIGBI, and
! uniform weights W for SMCRV.
!
h = 1.D0/DBLE(nm1)
DO i = 1 , n
xi = DBLE(i-1)h
X(i) = xi
Y(i) = xi
xi
YPTRUE(i) = 2.D0xi
IF ( i<n ) THEN
SIGMA(i) = 0.D0
B(1,i) = 1.D0
B(2,i) = Y(i)
B(3,i) = 2.D0
B(4,i) = YPTRUE(i)
B(5,i) = 1.D0
ENDIF
W(i) = 1.D0/(eps
eps)
ENDDO

Yes, if I put the anchor before the line EPS = 2.0D0*EPS, then no additional nudging is necessary.

SPAG is a great tool indeed! I’ve seen a lot of Fortran refactoring “research tools” that run to publish in a journal a solution that barely works on a couple of primitive test cases. Unlike them, SPAG is a way better not-so-well-known tool that can perform rapid massive Fortran refactoring in many aspects.

It’s a pity that EQUIVALENCE statements cannot be automatically unfolded. For me this error is a showstopper:

<>F COMMON cannot be converted to MODULE     COMMON /NAME/ is EQUIVALENCEd
SPAG Cannot continue
1 Like

Furthermore, the local variables definitions that participate in EQUIVALENCE statement are lost, and need to be recovered.

There are some minor oddities in transcripting FORMAT statements: SPAG eats spaces in the text after the line breaks, so the length spec screws up.

Overall, my conclusion on a big codebase is that the output of SPAG is 99% valid, and some minor issues are managable.Very cool, almost flawless! :heart:

Again, I could not enjoy COMMON blocks refactoring, which is my primary goal. So I depart to search for another solution.

Forgot one more oddity: SPAG screws up a job on multiple files, if they are NOT provided with .f or .f90 file extension. In my case these are include files with .COM extension. But handling them one by one works.

Hollerith constants is another major area of potential improvement. Although SPAG is able to convert Hollerith constants to character sequences, it creates more problems than solutions, because it introduces types mismatches all around between new character constants and their originally integer storages. SPAG does not attempt to re-adjust the types of variables holding the Hollerith constants during conversion. But fair enough I doubt that a robust way of doing it even exists.

The run-time trace facility in fpt will capture all RHS values to file and then compare the results on-the-fly with re-runs in different conditions. The record-replay facility will capture I/O transactions and named sub-program interfaces and replay them on re-runs under different operating systems, compilers and conditions. You can download fpt at http://simconglobal.com. Please contact me for a free academic licence.
John