Re-Structuring Legacy Code – No more GOTOs!
SPAG, the plusFORT restructuring tool, can unscramble ancient spaghetti Fortran code, and convert it to modern structured Fortran 95. It does this by reorganising code using a number of structured templates:
- block IFs
- named DO loops with CYCLE and EXIT
- replication of individual statements
- Internal subprograms (new in Version 8)
- Dispatch loop (new in Version 8)
Version 8, with two new templates, can now eliminate all GOTOs from even the most convoluted legacy code.
SPAG does not change the meaning of a program, or even the order in which statements are executed; it does change the way the program logic is written down, making it much easier to understand and maintain. Blocks of code are reordered so that logically related sections are physically close, and jumps in control flow are minimised. SPAG may also replicate small code fragments where this improves the re-structured code.
Upgrade Declarations
- SPAG allows you to switch programs to explicit typing by adding declarations for implicitly defined variables, and adding IMPLICIT NONE. Explicit typing allows your compiler to detect errors which might otherwise remain undetected for years. This option leaves existing declarations unchanged (apart from re-formatting)
- Alternatively, SPAG can rewrite the entire declaration section from scratch using Fortran 95 syntax. In this mode, SPAG can:
- Include new declarations for implicitly typed variables, and add an IMPLICIT NONE statement.
- Add INTENT qualifiers to dummy arguments based on how the argument is used.
- Convert non-standard REAL*8, INTEGER*1 and similar declarations to standard Fortran using KIND definitions in the standard ISO_FORTRAN_ENV module.
Modularization
SPAG can:
- Convert INCLUDE files and COMMON blocks to MODULEs, and insert USE statements as required.
- Create MODULEs containing explicit interfaces for all subprograms and insert USE statements as required.
- SPAG generates symbol tables which are used by GXCHK to examine the large scale calling structure and data usage within a program. GXCHK produces reports showing how subprograms and data can be moved into higher level subprograms or MODULEs (see below).


Code Re-formatting
SPAG contains a powerful code beautifier, with dozens of options controlling spacing, case, labels, indentation, use of CONTINUE etc. You can use SPAG to switch back and forth between the F77 and F95 source forms. But SPAG goes much further:
- Convert back and forth between old and new source forms, or write output in a style which is compatible with both.
- SPAG identifies, and optionally removes dead code (statements which could never be executed) and clutter (variables or PARAMETERs which are declared but never used).
- SPAG provides a simple and safe method for systematically changing the names of symbols within a program.
- SPAG allows you to specify how upper and lower case should be used to distinguish different types of symbol. For example, local variables may be lower case, PARAMETERs upper case, dummy arguments capitalised etc. (e.g. local , PARAM , Dummyarg, COMmonvar).
- SPAG can convert between old and new style relational operators.
- User may insert !-ANCHOR and !-ASIS directives in source code to guide SPAG restructuring and formatting.
There are over 100 configuration options which allow you to customise SPAG output to local conventions and requirements.
Examples
Example 1 – Before
Example 1 – After
Example 2 – Fortran 66
Example 2 – Fortran 77
Example 3 – Before
Example 3 – After
Example 1 – Original Spaghetti Code
IBON=0
IF(KON)35,19,35
19 IF(NSQ-56)24,22,24
22 IF(LSQ-46)5,28,5
24 IF(NSQ-55)29,27,29
27 IF(LSQ-45)5,28,5
28 IBON=2
GO TO 5
29 IF(LSQ-32)30,31,30
30 IF(LSQ-39)39,31,39
31 IBON=-5
GO TO 5
39 IF(LSQ-35)52,51,52
52 IF(LSQ-36)5,51,5
51 IBON=10
GO TO 5
35 IF(MARK(NMOVE))36,37,37
36 IBON=-5
GO TO 5
37 IBON=5
5 end
Example 1 – After Processing by SPAG
ibon = 0
IF ( kon.NE.0 ) THEN
IF ( mark(nmove).LT.0 ) THEN
ibon = -5
ELSE
ibon = 5
ENDIF
ELSEIF ( nsq.NE.56 ) THEN
IF ( nsq.NE.55 ) THEN
IF ( lsq.EQ.32 ) THEN
ibon = -5
ELSEIF ( lsq.EQ.39 ) THEN
ibon = -5
ELSEIF ( lsq.EQ.35 ) THEN
ibon = 10
ELSEIF ( lsq.EQ.36 ) THEN
ibon = 10
ENDIF
ELSEIF ( lsq.EQ.45 ) THEN
ibon = 2
ENDIF
ELSEIF ( lsq.EQ.46 ) THEN
ibon = 2
ENDIF
END
Example 2 – Original Fortran 66.
This subroutine picks off digits from an integer and branches depending on their value.
SUBROUTINE OBACT(TODO)
INTEGER TODO,DONE,IP,BASE
COMMON /EG1/N,L,DONE
PARAMETER (BASE=10)
13 IF(TODO.EQ.0) GO TO 12
I=MOD(TODO,BASE)
TODO=TODO/BASE
GO TO(62,42,43,62,404,45,62,62,62),I
GO TO 13
42 CALL COPY
GO TO 127
43 CALL MOVE
GO TO 144
404 N=-N
44 CALL DELETE
GO TO 127
45 CALL PRINT
GO TO 144
62 CALL BADACT(I)
GO TO 12
127 L=L+N
144 DONE=DONE+1
CALL RESYNC
GO TO 13
12 RETURN
END
Example 2 – Fortran 77 Version.
In addition to restructuring, SPAG has renamed some variables, removed the unused variable IP, inserted declarations, and used upper and lower case to destinguish different types of variable.
SUBROUTINE OBACT(Todo)
IMPLICIT NONE
C*** Start of declarations inserted by SPAG
INTEGER act , LENgth , NCHar
C*** End of declarations inserted by SPAG
INTEGER Todo , DONe , BASE
COMMON /EG1 / NCHar , LENgth , DONe
PARAMETER (BASE=10)
100 IF ( Todo.NE.0 ) THEN
act = MOD(Todo,BASE)
Todo = Todo/BASE
IF ( act.EQ.1 .OR. act.EQ.4 .OR.
& act.EQ.7 .OR. act.EQ.8 .OR.
& act.EQ.9 ) THEN
CALL BADACT(act)
GOTO 200
ELSEIF ( act.EQ.2 ) THEN
CALL COPY
LENgth = LENgth + NCHar
ELSEIF ( act.EQ.3 ) THEN
CALL MOVE
ELSEIF ( act.EQ.5 ) THEN
NCHar = -NCHar
CALL DELETE
LENgth = LENgth + NCHar
ELSEIF ( act.EQ.6 ) THEN
CALL PRINT
ELSE
GOTO 100
ENDIF
DONe = DONe + 1
CALL RESYNC
GOTO 100
ENDIF
200 RETURN
END
Example 2 – Fortran 95.
SPAG has used DO WHILE, SELECT CASE, EXIT and CYCLE. No GOTOs or labels remain. The COMMON block has been replaced by a MODULE, and all declarations rewritten using Fortran 95 syntax.
SUBROUTINE OBACT(Todo)
USE C_EG1
IMPLICIT NONE
!
!*** Start of declarations rewritten by SPAG
!
! PARAMETER definitions
!
INTEGER , PARAMETER :: BASE = 10
!
! Dummy arguments
!
INTEGER,INTENT(INOUT) :: Todo
!
! Local variables
!
INTEGER :: act
!
!*** End of declarations rewritten by SPAG
!
DO WHILE ( Todo/=0 )
act = MOD(Todo,BASE)
Todo = Todo/BASE
SELECT CASE (act)
CASE (1,4,7,8,9)
CALL BADACT(act)
EXIT
CASE (2)
CALL COPY
LENgth = LENgth + NCHar
CASE (3)
CALL MOVE
CASE (5)
NCHar = -NCHar
CALL DELETE
LENgth = LENgth + NCHar
CASE (6)
CALL PRINT
CASE DEFAULT
CYCLE
END SELECT
DONe = DONe + 1
CALL RESYNC
ENDDO
END SUBROUTINE OBACT
Example 3 – Dispatch Loops and Internal SUBROUTINES
| Before Restructuring |
|---|
FUNCTION IUNITS(JDIF,KDIF,JU,KU,IP)
INTEGER IP,IUNITS,JDIF,JU,KDIF,KU
IUNITS=1
IF(JDIF)20,30,40
20 JU=-1
IF(KDIF)50,60,70
30 JU=0
KU=KDIF/IABS(KDIF)
IP=4
RETURN
40 JU=1
IF(KDIF)70,60,50
50 IF(JDIF-KDIF)100,51,100
51 KU=JU
IP=3
RETURN
60 KU=0
IP=4
RETURN
70 IF(JDIF+KDIF)100,71,100
71 KU=-JU
IP=3
RETURN
100 IUNITS=0
RETURN
END
|
| spag iunits.for 31=1 (Dispatch Loops) | spag iunits.for 31=2 (Internal Subroutines) |
|---|---|
FUNCTION iunits(Jdif,Kdif,Ju,Ku,Ip)
IMPLICIT NONE
INTEGER Ip , iunits , Jdif , Ju , Kdif , Ku
INTEGER :: spag_nextblock_1
spag_nextblock_1 = 1
SPAG_DispatchLoop_1: DO
SELECT CASE (spag_nextblock_1)
CASE (1)
iunits = 1
IF ( Jdif<0 ) THEN
Ju = -1
IF ( Kdif<0 ) THEN
ELSEIF ( Kdif==0 ) THEN
spag_nextblock_1 = 2
CYCLE SPAG_DispatchLoop_1
ELSE
spag_nextblock_1 = 3
CYCLE SPAG_DispatchLoop_1
ENDIF
ELSEIF ( Jdif==0 ) THEN
Ju = 0
Ku = Kdif/iabs(Kdif)
Ip = 4
RETURN
ELSE
Ju = 1
IF ( Kdif<0 ) THEN
spag_nextblock_1 = 3
CYCLE SPAG_DispatchLoop_1
ENDIF
IF ( Kdif==0 ) THEN
spag_nextblock_1 = 2
CYCLE SPAG_DispatchLoop_1
ENDIF
ENDIF
IF ( Jdif/=Kdif ) THEN
iunits = 0
RETURN
ELSE
Ku = Ju
Ip = 3
RETURN
ENDIF
CASE (2)
Ku = 0
Ip = 4
RETURN
CASE (3)
IF ( Jdif+Kdif/=0 ) THEN
iunits = 0
ELSE
Ku = -Ju
Ip = 3
RETURN
ENDIF
EXIT SPAG_DispatchLoop_1
END SELECT
ENDDO SPAG_DispatchLoop_1
END FUNCTION iunits
|
FUNCTION iunits(Jdif,Kdif,Ju,Ku,Ip)
IMPLICIT NONE
INTEGER Ip , iunits , Jdif , Ju , Kdif , Ku
iunits = 1
IF ( Jdif<0 ) THEN
Ju = -1
IF ( Kdif<0 ) THEN
ELSEIF ( Kdif==0 ) THEN
CALL spag_block_1
RETURN
ELSE
CALL spag_block_2
RETURN
ENDIF
ELSEIF ( Jdif==0 ) THEN
Ju = 0
Ku = Kdif/iabs(Kdif)
Ip = 4
RETURN
ELSE
Ju = 1
IF ( Kdif<0 ) THEN
CALL spag_block_2
RETURN
ENDIF
IF ( Kdif==0 ) THEN
CALL spag_block_1
RETURN
ENDIF
ENDIF
IF ( Jdif/=Kdif ) THEN
iunits = 0
RETURN
ELSE
Ku = Ju
Ip = 3
RETURN
ENDIF
CONTAINS
SUBROUTINE spag_block_1
Ku = 0
Ip = 4
RETURN
END SUBROUTINE spag_block_1
SUBROUTINE spag_block_2
IF ( Jdif+Kdif/=0 ) THEN
iunits = 0
ELSE
Ku = -Ju
Ip = 3
RETURN
ENDIF
END SUBROUTINE spag_block_2
END FUNCTION iunits
|

