2 SPAG - Renovation & Analysis

2.1 Introduction

2.1.1 What does it do?

SPAG is a multi-purpose tool for analyzing and improving Fortran programs. It combines restructuring and re-formatting with translation to Fortran 95, and both static and dynamic analysis in a single powerful package. SPAG offers facilities which go beyond the sum of these parts. For example:

2.1.2 Code Restructuring

SPAG can unscramble spaghetti Fortran 66 code, and convert it to modern and structured Fortran 95.

When SPAG restructures a program, it does not change its meaning, 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. Old-fashioned control constructs (such as GOTO, arithmetic IF, computed GOTO etc.) are eliminated, or replaced by block IFs, DO loops with EXIT and CYCLE etc. SPAG also rearranges blocks of code so that logically related sections are physically close, and jumps in control flow are minimized. The algorithms used to do this draw on a deep analysis of the program structure.

2.1.3 Code Replication

Sometimes, rearrangement is not enough. Spaghetti programmers often made their programs more complex in order to re-use a few bytes of code - perhaps to avoid putting 'N=0' more than once. SPAG can undo the damage by replicating small code fragments where this improves the restructured code.

2.1.4 Dispatch Loops and Internal Subroutines

In some cases a final restructuring stage may be necessary to remove the last few GOTOs. SPAG can rationalise the most complex spaghetti by breaking the code into separately addressable blocks which are relocated within a SELECT CASE construct, the whole being executed within an outer dispatch loop. Transfers between blocks are effected by setting the value of the CASE variable after each block is executed, and cycling the dispatch loop. This effectively imposes a known structure on even the most unruly code.

A similar result may be acheived by moving the separately addressable blocks mentioned above into internal subroutines. GOTOs are replaced by CALLs to those internal subroutines, and in some cases they may be called recursively. This can be very effective, but carries a risk: there can be cases in which the recursion is arbitrarily deep, which may lead to stack overflow when the restructured program is executed. This is rare in practice, but users need to be aware of the risk.

SPAG offers both the above options, and a hybrid in which internal subroutines are preferred if recursion is not needed.

2.1.5 Translation to Fortran 95

In addition to translating source form, declaration style, and control constructs to Fortran 95, SPAG can also convert COMMON blocks and INCLUDE files to Fortran 95 modules. It can also create a module containing an INTERFACE block for each subprogram, and insert appropriate USE statements in calling subprograms. Fortran 95 compilers can use these constructs to perform argument checking.

2.1.6 Adding or Rewriting Declarations

Most older Fortran programs use IMPLICIT typing. That is to say that variables are not formally declared at the head of each subprogram, as they are in Pascal or C, but are simply assigned a type according to their initial letter. SPAG provides the means to convert such programs to explicit typing, which in Fortran is enforced using the IMPLICIT NONE statement. It does this by adding explicit declarations for all undeclared variables at the head of each subprogram. Explicit typing allows your compiler to detect errors which might otherwise remain undetected for years.

SPAG can also rewrite your declarations from scratch using either Fortran 77 or Fortran 95 declaration style. When SPAG does this, it groups declarations in a logical order. For example, dummy argument declarations form a separate section. When using the Fortran 95 declaration style, arrays with the same dimension are grouped together.

If declarations are rewritten in the Fortran 95 style, SPAG can add INTENT clauses, based on the way the argument is used within the current subprogram.

2.1.7 Translation of VAX Structures

Fortran 95 provides an ISO standard alternative to the VAX Fortran STRUCTURE and RECORD statements. SPAG can translate between the two forms. Features, such as UNIONs, which are not available in standard Fortran 95 are left unchanged, in a form suitable for a compiler which provides them as an extension.

2.1.8 Clutter Removal

Old programs often contain 'dead code' which can never be executed, and variables which are declared but never used. SPAG identifies and, optionally, removes this clutter.

2.1.9 Renaming Variables

SPAG provides a simple and safe method for systematically changing the names of symbols within a program. SPAG guards against the danger of choosing a new name which is already in use, and re-formats the source code as required.

2.1.10 Define a Corporate Style

SPAG contains a powerful code beautifier, with dozens of options controlling spacing, labels, indentation, use of CONTINUE etc. There is also an 'AS-IS' option for preserving the format of painstakingly hand-formatted declaration statements. SPAG can help make programs self-documenting, by using case to distinguish different types of symbol. For example, local variables may be lower case, PARAMETERs upper case, dummy arguments capitalized etc. (e.g. local, PARAM, Dummyarg, COMvar). All this gives project managers a powerful tool for defining and enforcing a corporate 'house programming style', making it easier for your programmers to work with each other's code, and with code from other sources.

2.1.11 Dynamic Analysis

If a bug is defined as 'an incorrect value in a storage location', then there is a large subset (perhaps 30%) which can be defined as 'an undefined value in a storage location', and many of the remainder may cause that condition as a knock-on effect. The 'Dynamic Analysis' option of plusFORT is a tool for diagnosing these errors at run-time. Probes are inserted in the source code before any operation which depends on the value of a data item. If the data item is undefined, the probes write details of the error to a log-file for later analysis. Source code for the probes is supplied.

A static analyzer such as GXCHK can detect a few of these errors, but the majority can only be detected at run-time. In fact static and dynamic analysis complement each other well - each excels where the other is deficient.

2.1.12 Coverage Analysis

In addition to testing for the definition status of variables and arrays, SPAG can insert probes to measure the time spent in individual subprograms, and monitor what code is executed. Over a series of runs, the probes identify untested code blocks and computational hot-spots. The CVRANAL utility produces a report which summarizes these results, and, if required, inserts usage counts back into the original code as comments (e.g. as end-of-line comments).

2.1.13 Symbol Table Files

SPAG optionally generates symbol table files which provide valuable information about data usage within subprograms. These files are used by GXCHK, the global cross check program (see Chapter 3), to generate an overview of data usage within an entire program.

Symbol tables are plain ASCII text files, the format of which is described in detail in Appendix A. Users are encouraged to develop their own applications using these files as input. For example it would be a simple matter to write a program to check conformance to local variable naming conventions.

2.2 Source Language

SPAG supports ISO standard Fortran 95 (and 77), VAX Fortran and IBM VS Fortran. SPAG also understands Fortran 2003, with a few minor exceptions, such as the ASSOCIATE construct. In addition, almost all the extensions supported by the major Windows, Mac and Linux compilers are supported.

Two minor exceptions to the above are:

An up to date summary of limitations and support for newer Fortran standards may be found in the READ.ME file in the plusFORT installation release directory.

2.3 Organizational Considerations

SPAG is designed to fit into any Fortran programming environment. However, in order to get the most from it, you may find it advantageous to observe the following guidelines:

2.4 The SPAG Command Line

SPAG can be run from a command prompt, or using PFFE, the plusFORT IDE.

From a command prompt, simply type spag followed by the name of the file(s) you wish to process. You can use wild-cards to specify more than one file. For example:

      SPAG A*.FOR B*.f90

(Windows)

      spag a*.f b*.f90

(Linux/Mac)

processes the contents of all files in the current directory which fit either wildcard.

It is generally simpler and better to use a single invocation of spag for multiple files, rather than separate invocations for each file. This is because spag performs a pre-scan of the input code which allows it to see a larger context. In particular, it reorders the input files, so that modules are processed before routines that USE them. Ordinarily - when using a compler for example - this reordering must be arranged externally - perhaps using a makefile, but SPAG's automatic reordering renders this unnecessary.

Under Windows, .for is assumed if no extension is specified (b* is treated as b*.for). The default file name is *.for. Thus, if you just type SPAG, all .for files in the current directory are processed.

On Linux and Mac, the extension must be specified, and there is no default file name.

By default, SPAG uses the filename extension to infer which source form is used. Thus, .for and .f files are assumed to use the old fixed source form, whereas SPAG assumes that .f90 and .f95 files use the newer free source form first defined in the Fortran 90 standard. For the fixed source form, SPAG analyses the source code to determine whether it conforms strictly to the standard (no tabs or long lines, possible sequence numbers in columns 73-80), or whether the more relaxed VAX-style format (long lines and tabs) should be assumed. This behaviour can be changed using configuration option 140.

The operation of SPAG may be modified by command line switches. Currently the following switches are available:

FIG=

specifies the configuration file name (see section 2.5). If you don't specify the configuration file name in this way, SPAG looks for a file called spag.fig. In either case, the search rules defined in section 1.4 are followed. SPAG halts with an appropriate message if no configuration file can be found.

If a '?' is appended (e.g. FIG=TOF90.FIG? or FIG=?), SPAG lists the contents of the active configuration file to the screen, with a pause after each screen full.

LOG=

specifies the name of a file to which a copy of all screen output is routed. If you don't specify a log-file name in this way, output is sent to a file called spag.log.

nnn=

specifies the value of item nnn of the SPAG configuration data (see 2.6). Leading zeros should be omitted from the item number (enter 1=2 rather than 001=2). Items specified in this way over-ride those read from the configuration file.

TO=

specifies the name of a single file for output of all modified source code. If TO= is not set, output may be routed to a single large file, to an output file for each input file, or to an output file for every sub-program, depending on items 10, 230 and 231 of the configuration data.

SYM=

specifies the name of a single file for output of all symbol tables. If SYM= is not set, output may be routed to a single large file, to an output file for each input file, or to an output file for every sub-program, depending on items 10, 232 and 233 of the configuration data.

IFC=

specifies the name of a single file for output of all Fortran 95 INTERFACE modules. If IFC= is not set, output may be routed to a single large file, to an output file for each input file, or to an output file for every sub-program, depending on items 11, 236 and 237 of the configuration data.

For example

      spag a*.f90 log=NEW.log fig=MARY.fig 1=1 60=1 sym=NEW.smb

This command causes SPAG to analyze all files in the current directory whose name fits the template a*.f90. A copy of all screen output is sent to NEW.log. Configuration options are read from MARY.fig, but the controls on records 1 and 60 are both set to 1 (re-format only, and leave specification statements unchanged). All symbol tables are written to the file NEW.smb. The way modified source code is output depends on items 10, 231 and 232 of the configuration data.

2.5 The SPAG Configuration File

The configuration file is a plain text file which provides the means by which users may customize SPAG to their own requirements and preferences. You can use an installation default configuration file, or create your own by copying one of the supplied versions and editing it using your standard editor.

PFFE, the plusFORT IDE includes a comprehensive editor for SPAG configuration files (including context-sensitive links to this manual). Many users find it easier to work using that editor, even when using the command line to invoke spag.

The file consists of data records and comment records. Any record with a space in column 1 is a comment record; all others are data records. Data records consist of an integer item number, followed by an '=' character and a value. They are terminated by a space character (optionally followed by a comment) or by the end-of-record. For items 1 to 199, the value is a simple integer. For items 200 to 299, the value is a text string. Records may appear in any order. Items 300 and up are ignored.

A small section from a typical configuration file is shown below. The significant parts are 1=3, 2=2 and 3=3, which set configuration items 1, 2 and 3 to 3, 2 and 3 respectively. The reaminder is commentary.

  #====================================#
  #======== Global Run Control ========#
  #====================================#

1=3    Type of Run
         0=no source output
         1=beautify only
         2=and relabel
         3=and restructure
         4=dynamic analysis

2=2    Symbol Table Ouptut
        -1=no symbol table
         0=generate table
         1=write to file
         3=Verbose

3=3    Clutter Checks
         0=no clutter checks
         1=checks only
         2-5=remove clutter

There are three sample configuration files in the plusFORT installation directory:

spag.fig

The input source form is inferred from the file extension. Default configuration options are used throughout.

tof90.fig

The input source form is inferred from the file extension. SPAG uses Fortran 90/95 features wherever appropriate

spag77.fig

The input source form is inferred from the file extension. SPAG uses Fortran 77 control constructs where possible.

The meaning of each data item is explained in detail in the next section.

2.6 SPAG Configuration Data

Item 1

Source Code Output

Specifies what source code output is required - possible values are:

0

No source code output. This option may be useful if SPAG is used to produce symbol tables and diagnostic output.

1

Reformat only. SPAG reformats the program, according to the controls in the remainder of the file, but does not restructure it or change any statement labels.

2

Reformat and re-label. SPAG reformats the program and reassigns statement labels according to the controls in items 80 to 90. It does not restructure the program.

3

Reformat, re-label and restructure. Restructuring is a far more radical procedure than reformatting or re-labelling; it can change the appearance of code completely. See section 2.1 for a discussion of the subject.

4

Dynamic analysis. SPAG produces code which contains probes to check for errors as the program runs. The code produced using this option is intended to be passed straight to a compiler. It will not be easy to understand or maintain. SPAG may override other configuration settings if this option is selected. See section2.8 for further discussion of dynamic analysis.

Item 2

Symbol Tables

Specifies whether the symbol tables generated by SPAG are to be written to files - possible values are:

0

No symbol table output. SPAG generates symbol tables for its own use, but does not write them to an output file.

-1

SPAG does not generate symbol tables. If this option is selected, some other options will be unavailable. In particular, the declaration standardization, clutter removal and symbol renaming facilities cannot be used, and the case of variable names in restructured output cannot depend on their usage. SPAG does not read the contents of INCLUDEd files if this option is activated. This option is not intended for general use.

1

Write symbol tables to file. The names of the output files are determined by items 10, 232 and 233. Their format is described in Appendix A. If this option is selected, symbol tables for SUBROUTINE and FUNCTION segments are made as small as possible. In particular variables and PARAMETERs which are defined in an INCLUDE file, but which are not otherwise referenced, are excluded from the output symbol table. This can reduce their size very considerably. GXCHK (see chapter 3) will work with this setting, but it may be unable to spot globally unreferenced PARAMETERs and COMMON variables. This problem can be overcome by ensuring that every INCLUDEd file appears either in the main program, or in a BLOCKDATA segment.

2

As above, but, where possible, additional information is appended to the symbol name. For example, a list of COMMON variables is appended to COMMON block names, and precise dimensioning information is appended to array names. The extra information is sufficient to allow regeneration of the declaration section using only data from the symbol table. This increases the size of the symbol tables somewhat, but has no effect on the operation of GXCHK. Further details may be found in Appendix A.

3

As above, but all symbols are included in the output file, including unreferenced COMMON variables and PARAMETERs from INCLUDE files. If this option is selected, the symbol tables may be much larger than otherwise. If every INCLUDEd file appears in the main program or a BLOCKDATA segment, there is no advantage, and some disadvantage to setting item 2 to 3.

Item 3

Clutter Checks

Specifies how SPAG should deal with clutter (unused variables, PARAMETERs, COMMON blocks etc.). Possible values are:

0

No clutter checks.

1

Check for unused local variables, dummy arguments, PARAMETERs, COMMON blocks and INCLUDE files. It is to be expected that there may be unused COMMON blocks and PARAMETERs defined within INCLUDE files, and such instances are not reported. INCLUDE files are reported as unused only if everything in them is unused.

In addition to finding variables which are totally unused, SPAG reports variables which are assigned a value, but are otherwise unused, and variables which are used in calculations even though they have not been assigned a value.

2

SPAG reports clutter and removes all references to unused, un-initialized local variables.

3

SPAG reports clutter and removes all references to unused local variables (except those initialized in a DATA statement) and PARAMETERs.

4

SPAG reports clutter and removes all references to unused local variables, PARAMETERs and COMMON blocks. SPAG does not remove COMMON blocks from the main PROGRAM or from a BLOCKDATA.

5

SPAG reports clutter and removes all references to unused local variables, PARAMETERs, COMMON blocks and INCLUDE files. SPAG does not remove COMMON blocks or INCLUDE files from the main PROGRAM or from a BLOCKDATA. This option should not be selected if INCLUDE files might contain IMPLICIT statements, or any other statement which affects declarations made elsewhere. For example, if a variable appears in a type statement in one file and a COMMON statement in another.

See section 2.7.8 for further discussion of clutter removal. Items 134-136 allow finer control of warning messages

Item 4

Declaration Standardization

Specifies whether and how SPAG should reorganize the declarations in a subprogram. Possible values are:

0

No change to declarations.

1

Insert explicit type declarations for variables which would otherwise be assigned a type by the Fortran IMPLICIT mechanism. Other declarations remain unchanged.

2

As above, but in addition an IMPLICIT NONE statement is inserted. Any existing IMPLICIT statement is removed, unless it occurs in an INCLUDE file.

3

The declaration section is rewritten from scratch using Fortran 77 declaration syntax. PARAMETERs, Dummy arguments, COMMON variables and local variables are placed in separate commented sections.

4

As above, but in addition an IMPLICIT NONE statement is inserted. Any existing IMPLICIT statement is removed, unless it occurs in an INCLUDE file.

5

The declaration section is rewritten from scratch using Fortran 95 declaration syntax. PARAMETERs, Dummy arguments, COMMON variables and local variables are placed in separate commented sections. Identically dimensioned declarations are grouped together where possible.

6

As above, but in addition an IMPLICIT NONE statement is inserted. Any existing IMPLICIT statement is removed, unless it occurs in an INCLUDE file.

SPAG does not insert or modify type declarations for variables which are defined in INCLUDEd files, unless it is converting them to modules - see item 14.

In previous versions of SPAG, there were options for adding 10, 20 or 40 to the base value of item 4, to control various aspects of SPAG's operation. These options are now available via items 13, 14, 15 and 16. SPAG can still accept configuration files in the old format, but it is recommended that its use be discontinued.

Item 5

Variable Renaming

Specifies whether SPAG should change the name of symbols before writing the restructured output file. Possible values are:

0

No name changes.

1

Change symbol names as specified in the 'renames file'. The name of the renames file is specified by item 211, and its format is described in section 2.7.9.

Item 6

Timing, Test Coverage and Hot-spot Analysis

Specifies whether SPAG should insert the statements specified in items 243-247 in such a way as to trace entries to and exits from subprograms and code blocks. The trace statements may be used to analyze test coverage (i.e. to identify parts of the program that have not been tested) and execution hot-spots. Possible values are:

0

Do not insert trace statements.

1

Insert trace statements as the first executable statement of every subprogram, and immediately before every RETURN or STOP. Trace statements are also inserted before END statements which act as RETURN or STOP. With this setting, the modified code automatically produces a report when the run completes showing the total time spent in each subprogram.

2

In addition, insert trace statements (as specified using item 247) at the start of every linear code block. If this option is selected, item 234 (coverage data directory) must also be set, as SPAG creates coverage data files in the specified directory.

See section 2.9 for further discussion of coverage analysis.

Item 7

Complexity Metrics

Specifies whether SPAG should compute complexity metrics for each subprogram. Possible values are:

0

Do not compute complexity metrics

1

Compute complexity metrics for each subprogram. If SPAG is restructuring the code, it computes 'before and after' values.

SPAG computes three metrics, each of which ranges in value from 0 to 100, with higher values indicating greater complexity. A target value is suggested for each metric, but it is accepted that there are circumstances when well written code exceeds the targets. The three metrics reported by SPAG are:

  • LOGICAL CONTENT. This is a measure of the number of decision points (such as IF or ELSEIF statements) in the subprogram. The target value is 50 (corresponding to a McCabe Cyclomatic Complexity of around 10). This metric can be reduced by splitting up large subprograms. Restructuring has little effect on it.
  • ALL CONTROL JUMPS. This is a measure of how discontinuous control flow is in the subprogram. Any GOTO or other command which transfers control to a different point in the source code contributes to this metric. The target value is 20. Restructuring often reduces this metric appreciably.
  • UNSTRUCTURED JUMPS. Similar to ALL CONTROL JUMPS except that jumps from within a DO loop to or just past the terminal statement (using GOTO, CYCLE or EXIT), and jumps to a RETURN, STOP or END statement are excluded. The target value is 0. Restructuring often reduces this metric appreciably.

Item 8

Interface Summary Reports

Specifies whether SPAG should prepare the ground for GXCHK to embed an interface summary as comments in the restructured source code. This summary enumerates calls in and out of each subprogram, and details arguments and COMMON data shared with the remainder of the program. SPAG inserts a marker in output code which GXCHK subsequently replaces with the interface summary. Interface summaries are described in detail in section 3.6. Possible values are:

0

No interface summary reports

1

Prepare for summary reports by inserting a marker comment in the restructured code.

Item 9

Source File re-ordering

By default, SPAG (7.30 or later) performs a rapid initial scan of the source code, and, if necessary reorders the input files so that modules are processed before code that USEs them. This ensures that, so far as is possible, up to date symbol files for USEd modules are available when SPAG processes a file. If item 9 is not set to 0, this pre-scan is suppressed, and files are processed in the order they are presented.

If the list of input files does not contain all of the modules that appear in USE statements, SPAG will look in a file called PFMODULE.KEY for the locations of symbol files produced in earlier runs. Further details of this process may be found in Section 2.10. If the symbol file is absent or obsolete, SPAG will be unable to perform a full and accurate analysis.

Possible values are:

0

Perform a rapid pre-scan, and if necessary, re-order input files to ensure that modules are processed before code that uses them.

1

No pre-scan. Input files are processed in the order they are presented. This is the default behaviour in SPAG 7.25 and earlier.

Item 10

Output Files

Specifies how output files (both restructured source and symbol tables) are to be created. Possible values are:

0

Send all output to a single file. By default, the file name is spag.out for restructured source and spag.smb for symbol tables. These defaults can be over-ridden using the TO= and SYM= keywords on the command line.

1

Create new output files for each input file. For example, if your input file is called main.for, the output files would use the same filename (main), but with different extensions (e.g. main.new and main.smb). This is the recommended option.

2

Create new output files for each subprogram. For example, if your input file contains two subprograms called MAIN and CALC, there would be separate restructured source and symbol table files for each, perhaps called main.new, calc.new, main.smb and calc.smb. If this option is used, it is important to ensure that PROGRAM and BLOCKDATA subprograms are assigned a unique name.

For the last two options, you may specify the directory in which the new files are created (see items 230 and 232) as well as the extensions used (see items 231 and 233).

Item 11

Interface Module Output

Specifies how newly created subprogram interface modules are to be written (item 15 controls whether SPAG creates new interface modules). Possible values are:

0

Send all output to a single file. By default, the file name is intfaces.xyz (where .xyz is the extension specified in item 237 of spag.fig). This default can be over-ridden using the IFC= keyword on the command line.

1

Create new output files for each input file. For example, if your input file is called main.for, the output would be sent to a file called s_main.xyz.

2

Create new output files for each subprogram. For example, if your input file contains two subprograms called MAIN and CALC, there would be separate restructured source and symbol table files for each, perhaps called s_main.xyz and s_calc.xyz.

For the last two options, you may specify the directory in which the new files are created and the extension used (see items 236 and 237).

Item 12

Safe Functions

Specifies whether functions whose names do not appear in the file specified using item 210 can be regarded as 'safe'. A 'safe' function is one which simply returns a value, dependent on its arguments; it does not modify its arguments or any COMMON or SAVEd variable, or perform any input or output operation. The file specified using item 210 contains a list of INTRINSIC functions, and a user specified list of safe functions.

One point of the distinction is that the static analysis in SPAG and GXCHK is more informative if functions are known to be safe. Another is that, if SPAG restructuring produces code which executes a function more often than the original unstructured code, it can only be sure that the program logic is correct if the functions involved are 'safe'. This situation arises rarely in practice, and SPAG issues a warning message if there is any danger.

Possible values are:

0

Functions are regarded as unsafe unless their name appears in the INTRINSIC and safe functions file whose name is specified using item 210.

1

All functions are regarded as safe.

Item 13

Conversion of isolated COMMON to Fortran 95 modules

If item 13 is greater than 0, SPAG converts COMMON blocks which are not in an INCLUDE file to modules. This is not recommended without careful preparation, because COMMONs specified in this way may be different in every subprogram. It is quite possible that variables of one type in subroutine A are effectively EQUIVALENCEd to arrays of a different type in subroutine B. This would make SPAG's conversion invalid.

This option is only available when Fortran 95 style declaration rewriting is active (item 4 is 5 or 6). It is intended for experts only. Leave this value at 0 unless you understand the risks and are prepared to hand-edit corrections to the output produced by SPAG. See sections 2.7.4-6 for further detail.

Item 14

Conversion of INCLUDE file to Fortran 95 modules "on the fly"

If item 14 is set greater then 0, SPAG attempts to replace INCLUDE files with Fortran 95 modules. This is intended for use when INCLUDE files contain COMMON block declarations and associated type and PARAMETER delcarations. It should not be used if INCLUDE files contain executable code, or local variable declarations. For conversion of COMMON blocks which are not specified in an INCLUDE file, see item 13 above. It is recommended that users review sections 2.7.4-6 before using either of these options. This option is only available when Fortran 95 style declaration rewriting is active (item 4 is 5 or 6)

Item 15

Creation of FORTRAN 95 INTERFACE modules.

If item 15 is set greater than 0, SPAG creates MODULEs containing Fortran 95 INTERFACE definitions for every subprogram (see Item 11 for details of file output). In addition SPAG inserts USE statements in the calling subprograms as necessary. It is recommended that users review section 2.7.7 before using this option. This option is only available when Fortran 95 style declaration rewriting is active (item 4 is 5 or 6)

Item 16

Add INTENT to dummy arguments

If item 16 is set greater than 0, SPAG adds Fortran 95 INTENT clauses on to the dummy argument declarations, based on how the argument is actually used in the current subprogram. If SPAG is unable to determine the INTENT, for example where the argument is itself passed as an argument to another subprogram, SPAG does not add an INTENT clause. This option is only available when Fortran 95 style declaration rewriting is active (item 4 is 5 or 6)

Item 18

Configuration Options in Header

Specifies whether a header section containing a summary of options specified in spag.fig is to be inserted at the head of each subprogram in the restructured output file. Possible values are:

0

No header section.

1

Insert header section. Please use this option when sending material to our technical support department.

Item 20

Computed GOTO

Specifies whether computed GOTOs are to be converted to IF..THEN..ELSE or SELECT CASE constructs. Possible values are:

0

Leave computed GOTOs unchanged.

1

Change computed GOTOs into block IFs. If you choose this option, the test expression may be evaluated more than once; this could have two undesirable effects:

  • If the construct is inside a tight time-sensitive loop, the SPAG output program may run slower. However this effect is much more often imagined than real.
  • If the test expression in the computed GOTO statement accesses an 'unsafe' FUNCTION subprogram (see items 12 and 210) then the SPAG output program may be logically changed. SPAG issues a warning when this possibility arises.

2

Change computed GOTOs into Fortran 95 SELECT CASE constructs. Note that you can also use SPAG to convert most SELECT CASE constructs back to Fortran 77 (see item 21).

Computed GOTO Example

! item 20 = 0
      GOTO (10,20,30),i
 10   x = a
      GOTO 40
 20   x = b
      GOTO 40
 30   x = c
 40   CONTINUE
! item 20 = 1
      IF  ( i.EQ.2 ) THEN
         x = b
      ELSEIF( i.EQ.3 ) THEN
         x = c
      ELSE
         x = a
      ENDIF
! item 20 = 2
      SELECT CASE (i)
      CASE (2)
         x = b
      CASE (3)
         x = c
      CASE DEFAULT
         x = a
      END SELECT
 

The translations deal correctly with the case where i is less than 1 or greater than 3.

Item 21

SELECT CASE

Specifies whether SPAG should convert SELECT CASE constructs to equivalent standard Fortran 77 block IF constructs. SELECT CASE is a Fortran 95 construct which is also supported by some Fortran 77 compilers.

Possible values are:

0

No changes.

1

Convert SELECT CASE constructs to standard Fortran 77 using block IF constructs. SPAG guarantees to convert SELECT CASE constructs which were created by it, and many that were not. It will not convert constructs where the case expression is not of type INTEGER, or where any CASE statement contains a range specification (e.g. CASE (5:10) ).

Item 22

Arithmetic IF

Specifies whether arithmetic IFs are to be converted to IF..THEN..ELSE constructs. Possible values are:

0

Leave arithmetic IFs unchanged.

1

Change arithmetic IFs into block IFs. If you choose this option, and if the three labels in the arithmetic IF are all different, the test expression will be evaluated twice (instead of once); this could have two undesirable effects:

  • If the construct is inside a tight time-sensitive loop, the SPAG output program may run slower. However this effect is much more often imagined than real.
  • If the test expression in the arithmetic IF statement accesses an 'unsafe' FUNCTION subprogram (see items 12 and 210) then the SPAG output program may be logically changed. SPAG issues a warning when this possibility arises.

Arithmetic IF Translation Example

! item 22 = 0
       IF ( i ) 10,20,30
 10    x = a
       GOTO 40
 20    x = b
       GOTO 40
 30    x = c
 40    CONTINUE
! item 22 = 1
       IF ( i.LT.0 ) THEN
          x = a
       ELSEIF ( i.EQ.0 ) THEN
          x = b
       ELSE
          x = c
       ENDIF

Item 23

Logical IF + GOTO

Specifies whether logical IF followed by GOTO is to be converted to a block IF..THEN..ELSE construct. Possible values are:

0

Leave logical IF + GOTO unchanged.

1

Change logical IF + GOTO into block IF. Depending on the precise nature of the program logic, the new block IF may have ELSE or ELSEIF clauses, or may itself become a subsidiary ELSEIF clause of another block IF. SPAG may also need to reverse the sense of the logical expression in the IF statement (e.g. A.LT.B becomes A.GE.B). If item 24 is set appropriately, and if the program logic is in the appropriate form, the logical IF may be converted to a Fortran 95 style DO WHILE block.

Logical IF + GOTO Example

! item 23 = 0
      IF ( q1 ) GOTO 10
      x = a
      y = b
      GOTO 20
 10   x = b
      y = a
 20   CONTINUE
! item 23 = 1
      IF ( q1 ) THEN
         x = b
         y = a
      ELSE
         x = a
         y = b
      ENDIF

Item 24

DO WHILE

Specifies whether SPAG should make use of the DO WHILE construct to unscramble code containing backward pointing GOTOs. SPAG can also convert the DO WHILE construct back to standard Fortran 77.

Possible values are:

-3

Convert all existing DO WHILE constructs to standard Fortran 77 using backward pointing GOTO statements.

-2

Convert existing DO WHILE constructs to standard Fortran 77 if the DO statement is "DO WHILE (.TRUE.)" OR if the conversion does not require that a new GOTO statement be introduced.

-1

Convert existing DO WHILE (.TRUE.) constructs only.

0

Don't change either way. Existing DO WHILE blocks and backward pointing GOTOs remain unchanged.

1

Convert backward pointing GOTOs to DO WHILE constructs if the change can be made simply by converting a block IF to a DO loop, and no new statements are required.

DO WHILE Example 1

! Before conversion
 10   IF ( i.GE.0 ) THEN
         READ * , I
         CALL sub(i)
         GOTO 10
      ENDIF
! After conversion
      DO WHILE ( i.GE.0 )
         READ * , I
         CALL sub(i)
      ENDDO

2

Convert backward pointing GOTOs to DO WHILE constructs if the change can be made simply by converting a block IF to a DO loop, and no more than one new statement (a GOTO, CYCLE or EXIT) is required.

DO WHILE Example 2

! Before conversion
 5    IF ( i.GE.0 ) THEN
         READ * , i
         CALL sub(i)
         IF ( q ) GOTO 5
         CALL sub(i+1)
      ENDIF
! After conversion
      DO WHILE ( i.GE.0 )
         READ * , i
         CALL sub(i)
         IF ( .NOT.q ) THEN
            CALL sub(i+1)
            EXIT
         ENDIF
      ENDDO

3

Convert backward pointing GOTOs to DO WHILE constructs, even if this involves inserting bare DO (i.e. DO forever) and other statements.

DO WHILE Example 3

! Before conversion
 10   READ(9,END=100) z
      PRINT * , z
      GOTO 10
! After conversion
      DO
         READ(9,END=100) z
         PRINT * , z
      ENDDO

Item 25

EXIT and CYCLE

Specifies whether SPAG should use the Fortran 95 style EXIT (GOTO statement after DO loop terminator) and CYCLE (GOTO end of loop) statements. Possible values are:

0

Don't use EXIT or CYCLE. Replace any existing EXIT or CYCLE with an equivalent GOTO.

1

Use EXIT but not CYCLE. Where possible convert GOTO to EXIT. Replace any existing CYCLE with an equivalent GOTO.

2

Use CYCLE but not EXIT. Where possible convert GOTO to CYCLE. Replace any existing EXIT with an equivalent GOTO.

3

Use both EXIT and CYCLE. Where possible convert GOTO to EXIT or CYCLE.

EXIT and CYCLE Example

! Item 25 = 0
      DO i = 1 , 80
         ch = str(i:i)
         IF ( ch.EQ.' ' ) THEN
            IF ( lt.LE.0 ) GOTO 10
            GOTO 99999
         ELSEIF ( alp(ch) ) THEN
            nalp = nalp + 1
         ENDIF
         lt = lt + 1
 10   ENDDO
99999 END
! Item 25 = 3
      DO i = 1 , 80
         ch = str(i:i)
         IF ( ch.EQ.' ' ) THEN
            IF ( lt.LE.0 ) CYCLE
            EXIT
         ELSEIF ( alp(ch) ) THEN
            nalp = nalp + 1
         ENDIF
         lt = lt + 1
      ENDDO
      END

When SPAG inserts an EXIT or CYCLE in nested DO loops, it may add a DO loop name to the EXIT/CYCLE statement, and, if necessary to the corresponding DO and ENDDO statements. If the DO loop is not already named, SPAG will create a name using the prefix specified in configuration item 238.

Item 26

Statement Replication

Specifies whether SPAG may replicate individual statements if this improves the readability of a section of code. Possible values are:

0

Disallow code replication.

1

SPAG replicates statements as required. It does not prompt for permission, or insert explanatory comments in the output code.

2

SPAG replicates statements as required, and inserts an explanatory comment in front of each replicated statement.

3

SPAG prompts for permission before replicating a statement. If the replication is permitted, SPAG inserts an explanatory comment in front of each replicated statement.

Statement Replication Example

! original
       GOTO (10,20,30),i
       goto 50
 10    x = a
       GOTO 40
 20    x = b
       GOTO 40
 30    n = 0
       GOTO 50
 40    n = n + 1
 50    end
! item 26 = 0
      IF ( i.EQ.1 ) THEN
         x = a
         GOTO 100
      ELSEIF ( i.EQ.2 ) THEN
         x = b
         GOTO 100
      ELSEIF ( i.EQ.3 ) THEN
         n = 0
      ENDIF
      GOTO 99999
 100  n = n + 1
99999 END
! item 26 = 1
      IF ( i.EQ.1 ) THEN
         x = a
         n = n + 1
      ELSEIF ( i.EQ.2 ) THEN
         x = b
         n = n + 1
      ELSEIF ( i.EQ.3 ) THEN
         n = 0
      ENDIF
      END
 

In this example, structure is improved by duplicating the statement n = n + 1.

Item 27

Relocation into DO loop

Specifies whether blocks of code can be relocated into a DO loop from outside. This control does not apply to DO WHILE loops. Relocation into a DO WHILE loop is always allowed. Possible values are:

0

Do not relocate code into a DO loop.

1

Allow relocation into a DO loop.

Relocation Into a DO loop Example

! Item 27 = 0
      DO 10 i = 1 , 100
         IF ( q1 ) GOTO 20
 10   CONTINUE
      GOTO 30
 20   CALL sub1
 30   CALL sub2
! Item 27 = 1
      DO 10 i = 1 , 100
         IF ( q1 ) THEN
            CALL sub1
            GOTO 20
         ENDIF
 10   ENDDO
 20   CALL sub2

If you activate this option, and if the relocated code re-defines the DO loop variable (e.g. i = 0 instead of CALL sub1), then the resulting code violates the ISO standard (code within a DO loop is not allowed to redefine the DO loop variable). This should not cause any errors at run time, as the DO loop redefinition is followed immediately by a GOTO which exits the DO loop. However, many compilers will, quite properly, report an error. To avoid this eventuality, set item 27 to 0, or hand check the output code.

Item 28

Relocate RETURN or STOP followed by END

Specifies whether a block of code terminated by RETURN or STOP and followed by END may be relocated. Activating this option prevents SPAG from moving a RETURN or STOP which is the last executable statement in a subprogram. Possible values are:

0

Always allow relocation of blocks terminated by RETURN or STOP.

1

Disallow relocation of blocks terminated by RETURN or STOP and followed by END.

Relocation of RETURN Example

! original code
 11   call sub0
      If ( q1) then
         goto 10
      else
         call sub1
         call sub2
         goto 11
      endif
 10   return
      end
! Item 28 = 0
 100  CALL sub0
      IF ( q1 ) THEN
         RETURN
      ELSE
         CALL sub1
         CALL sub2
         GOTO 100
      ENDIF
      END
! Item 28 = 1
 100  CALL sub0
      IF ( .NOT.q1 ) THEN
         CALL sub1
         CALL sub2
         GOTO 100
      ENDIF
      RETURN
      END
 

Item 29

Remove Unreachable Code

Specifies whether blocks of code which could never be executed are to be removed. Possible values are:

0

Do not remove unreachable code, but issue a message detailing the problem.

1

Remove unreachable code.

Unreachable Code Example

      GOTO 200
! The following statement could never be executed
      WRITE(22,100) a,b,c
100   FORMAT(3F5.3)
200   PRINT *,a,b,c

Item 30

Convert VAX Structures

Specifies whether SPAG should translate VAX STRUCTURE and RECORD statements, and the corresponding structure references, to Fortran 95 derived types. Possible values are:

0

Leave VAX structures alone.

1

Convert VAX structures to Fortran 95 derived types.

Fortran 95 has no equivalent to the VAX MAP and UNION constructs, which are used make different elements of a structure share the same memory. When SPAG encounters a MAP or UNION construct, it leaves it intact, in a form acceptable to compilers which provide MAP and UNION as an extension to standard derived types. An example of this follows:

VAX Structure Example

!  VAX Structures
   structure /date/
   union
      map
         integer d , m , y
      end map
      map
         integer ddmmyy(3)
      end map
   end union
   end structure

   record /date/ today
   today.d = 20
   print * , today.ddmmyy(1)
!  Fortran 95 Derived Types
   TYPE DATE
      UNION
         MAP
            INTEGER D , M , Y
         ENDMAP
         MAP
            INTEGER DDMMYY(3)
         ENDMAP
      ENDUNION
   END TYPE DATE

   TYPE (DATE) today
   today%d = 20
   PRINT * , today%ddmmyy(1)

Item 31

Dispatch Loops and Internal Subroutines

Specifies whether SPAG should restructure code with GOTOs which remain after applying other techniques using Dispatch Loops and/or Internal Subroutines. This option is effective only if item 1 of the configuration data is set to 3.

0

Don't restructure using dispatch loops or internal subroutines.

1

SPAG identifies separately addressable code blocks, and relocates them into a SELECT CASE construct for execution in the correct sequence, under control of a dispatch loop

DO loops are treated as unbreakable units, but each DO may contain its own dispatch loop, which occasionally leads to nested dispatch loops.

Schematic Dispatch Loop Example

   INTEGER :: spag_nextblock_1
   spag_nextblock_1 = 1
   SPAG_DispatchLoop_1: DO
      SELECT CASE (spag_nextblock_1)
      CASE (1)
         block 1
         IF ( cond ) THEN
            SPAG_next_block_1 = 2
         ELSEIF ( cond2 ) THEN
            SPAG_next_block_1 = 3
         ENDIF
      CASE (2)
         block 2
         SPAG_next_block_1 = 3
      CASE (3)
         block 3
         IF ( cond2 ) EXIT SPAG_DispatchLoop_1  
         block 3 continued
         IF ( cond1 ) THEN
            SPAG_next_block_1 = 2
         ELSEIF ( cond2 ) THEN
            SPAG_next_block_1 = 1
         ENDIF
      CASE (4)
         ...
      END SELECT
   ENDDO SPAG_DispatchLoop_1

2

SPAG selects between the use of dispatch loops and internal subroutines on a subprogram by subprogram basis. Internal subroutines are preferred if they are possible, and do not involve the use of recursion, and if the input code does not contain any of the constructs specified under option 4 below. Otherwise dispatch loops are preferred.

3

As option 2, but recursive internal subroutines are allowed.

4

This is currently an experimental option, for use by experienced users. SPAG identifies separately addressable code blocks, and relocates them into internal subroutines. Blocks are invoked in the correct order using CALL statements, and the calls may be recursive. This option is ineffective if the subprogram being processed is, itself, an internal subroutine or function.

There are a number of known conditions which may cause this option to generate invalid code. These are:

  • END= and ERR= clauses in I/O statements.
  • FORMAT statements used by multiple I/O statements.
  • ASSIGNed GOTO statements.
  • CALLs to subprograms with alternate returns.

For example, if a single FORMAT statement is used in multiple internal subroutines, SPAG does not copy it to all the subroutines that use it. Compilers generally identify problems like this quickly, and they are easy to fix by hand. A future release of SPAG will address these issues, but in the meantime, this option should only be used by users prepared to hand edit minor problems, or backtrack to another option in the event of failure.

Refer to section 2.7.1 for more discussion of this and other restructuring options.

Item 35

Dynamic Analysis Element Size

Specifies the minimum element size (in bytes) for dynamic analysis. SPAG will not insert probes to check the definition status of numeric data items which are smaller than the specified minimum size. If this item is set to a small value (e.g. 1) there is a possibility of 'false positives' (i.e. the probe logs an error even though there is none). If it is set to a large value (e.g. 4), the probes may miss errors which could be found with a smaller value.

The best approach is probably to set item 35 to 1, but to increase it to 2 or 4 if false positives are a problem.

This control applies to numeric and logical data, and NOT to character data. See section 2.8 for further discussion of dynamic analysis.

Item 36

Static Local Variables

Specifies whether local variables should be assumed to be static. The ISO standard specifies that local data which is not specified in a SAVE or DATA statement becomes undefined when control is returned to the calling subprogram. However many compilers allocate local data statically, and many non-standard programs depend on static allocation of local data. Dynamic analysis of such programs is only possible if this item is set to 1.

Possible values are:

0

Assume local variables are dynamically allocated. In a dynamic analysis version of the code, local variables are set to the undefined value every time a subprogram is invoked.

1

Assume local variables are statically allocated. In a dynamic analysis version of the code, local variables are set to the undefined value the first time a subprogram is invoked, and are assumed to retain their value thereafter.

See section 2.8 for further discussion of dynamic analysis.

Item 40

Relocate FORMAT statements

Specifies whether FORMAT statements are to be grouped together just before the END statement of each subprogram. Possible values are:

0

Leave FORMAT statements in place.

1

Move all FORMAT statements to the end of the subprogram.

2

FORMAT statements are relocated at the end of subprogram if they are used more than once. FORMATs which are used only once are left in place.

Item 41

Use of CONTINUE

Specifies whether CONTINUE statements should be inserted whenever a label is required (except on FORMAT statements). Possible values are:

0

Remove all CONTINUEs other than DO loop terminators.

1

Insert a CONTINUE wherever a label is needed. Only CONTINUE and FORMAT statements have labels.

In general a value of 0 leads to code which is easier to understand, while a value of 1 gives code which is easier to modify.

CONTINUE Example

! item 41 = 0
      DO 10 i = 100 , 1 , -1
         IF ( qa(i) ) GOTO 12
 10   CONTINUE
 12   last = i
! item 41 = 1
      DO 10 i = 100 , 1, -1
         IF ( qa(i) ) GOTO 12
 10   CONTINUE
 12   CONTINUE
      last = i

This control is effective only for re-structuring runs (Item 1).

Item 42

Single statement block IFs

Specifies whether block IFs are to be converted to logical IFs if the block contains only one statement. Possible values are:

-1

All logical IF statements are converted to single statement block IFs.

0

Convert logical IF () GOTO statements to single statement block IFs.

1

Convert single statement block IFs to logical IFs if the statement is a GOTO.

2

All single statement block IFs are converted to logical IFs.

Single Statement block IF Example

! Item 42 = 1
      IF ( Q1 ) THEN
         X = Y
      ENDIF
! Item 42 = 2
      IF ( Q1 ) X = Y  

Item 43

Logic Reversal in block IF

Specifies whether constructs of the form

      IF ( a.EQ.b ) THEN
      ELSE
         X = Y
         ..
      ENDIF

should be converted to

      IF ( a.NE.b ) THEN
         X = Y
         ..
      ENDIF

Possible values are:

0

Leave the code unchanged.

1

Convert by removing the ELSE statement and reversing the sense of the IF statement test expression.

Where possible, SPAG reverses the sense of the test expression by changing logical and relational operators appropriately. In other cases, for example where a simple logical variable is used as the test expression, SPAG uses the .NOT. operator.

Item 44

Insert Redundant ELSE

Specifies whether SPAG should add a null ELSE block to block IFs that have an ELSEIF but no ELSE. Possible values are:

0

Do not insert redundant ELSE.

1

inserts a blank ELSE block in block IFs that contain ELSEIF block(s) but no ELSE. SPAG also ensures that SELECT CASE blocks include a CASE DEFAULT block.

Item 45

DO loops

Specifies which style of DO loop is to be used. Possible values are:

0

Always use of CONTINUE as the DO loop terminator (in conformance with standard Fortran 77)

1

Use CONTINUE for indexed DO loops, and ENDDO for DO WHILE loops.

2

Always use ENDDO as the DO loop terminator.

DO loop Example

! Item 45 = 1
      DO 100 i = 1 , n
         sum = sum + x(i)
 100  CONTINUE
! Item 45 = 3
      DO i = 1 , n
         sum = sum + x(i)
      ENDDO

Item 46

Character Constants

Specifies whether character constants are to be converted to a different format. In addition to the standard apostrophe delimited form (e.g. 'word'), SPAG recognizes Hollerith constants (of the form 4Hword), and quoted strings (of the form "word"). Hollerith constants need not be supported by an ISO Fortran 77 (or 95) compiler, though in practice, most do. Quoted strings are standard Fortran 95, and are supported by all recent compilers. Possible values are:

0

Convert Holleriths, but not quoted strings. Thus, 4Hword is converted to 'word', but "word" is left unchanged.

1

Convert both Holleriths and quoted strings to F77 standard form. Both 4Hword and "word" are converted to 'word'.

2

Convert quoted strings, but not Holleriths. Thus "word" is converted to 'word', but 4Hword is left unchanged.

SPAG deals correctly with apostrophes within Holleriths and quoted strings. Thus "can't" and 5Hcan't are converted to 'can''t'.

Item 47

Fortran 95 Relational Operators

Specifies whether the new Fortran 95 style relational operators are to be used. Possible values are:

-1

Relational operators are left unchanged.

0

Use the standard Fortran 77 relational operators .LT., .LE., .EQ., .NE., .GE., and .GT.). Any Fortran 95 relational operators are converted to this form.

1

Use the new Fortran 95 relational operators (<, <=, ==, \=, >= and >). Any Fortran 77 relational operators are converted to this form.

Both styles of operator are permitted by the Fortran 95 standard.

Item 48

Fortran 95 END SUBROUTINE statements

Specifies whether SPAG should translate END statements to Fortran 95 END SUBROUTINE, END PROGRAM, END FUNCTION etc. Possible values are:

0

Use a simple Fortran 77 style END statement

1

Use END SUBROUTINE, END PROGRAM, END FUNCTION etc.

2

In addition, append the subprogram name (END SUBROUTINE SUB1 etc.)

Item 49

Remove Redundant RETURN and STOP

Specifies whether SPAG should remove redundant RETURN statements which occur before the END statement in subprograms, and redundant STOP statements which occur before the END statement in a main program. Possible values are:

0

Leave RETURN and STOP alone.

1

Remove redundant RETURN and STOP before END.

Item 51

Input Statement Length

Specifies the input statement length for fixed form source. Any text after the specified position on input statement lines is discarded.

For free source form code, the statement length is 5120, unless this item has a value of 132 or more, in which case the specified value is used. SPAG issues a warning message if free form source is truncated because it extends beyond the specified statement length. Free source form is selected if item 140 is set to 1, or if set to zero, and the input source file extension is .f90 or .f95.

For fixed form source, a value of 72 is required if the input code contains sequence numbers in columns 73-80. Comments are not truncated. The maximum permitted value is 5120.

If you want to retain the sequence numbers in your restructured code, there are two approaches you might take. The first is to use item 60 and the !-ASIS directive to force SPAG to leave statements alone. However, this is not a safe procedure when applied to executable statements (see section 2.7.10). The second alternative is to convert sequence numbers to end-of-line comments by inserting a '!' in column 72 using a text editor. End-of-line comments using '!' are permitted in standard Fortran 95 but older Fortran 77 compilers may reject them.

Item 53

Old-style Direct Access

Specifies whether unpaired apostrophes as used in old-style direct access (using DEFINE FILE, FIND etc.) are to be allowed. Possible values are:

0

Disallow unpaired apostrophes. This setting is recommended unless you know that old-style direct access is used.

1

Allow unpaired apostrophes in old-style direct access statements.

Item 54

PDP style PARAMETER

Specifies whether PDP style PARAMETER statements, with no enclosing brackets, are to be allowed. Note that many compilers will interpret PDP style PARAMETER statements as assignment statements.

      PARAMETER vdim=10

may be interpreted as

      PARAMETERVDIM = 10

Possible values are:

0

Interpret PDP style PARAMETER statements as assignment statements. This setting is recommended unless you know that PDP style PARAMETERs are used.

1

Allow PDP style PARAMETER statements.

PDP style PARAMETERs also differ from standard PARAMETERs in that the type of the constant determines the type of the PARAMETER. Thus in the above example, VDIM is of type INTEGER, because 10 is an integer. It would be an error to declare VDIM to be of a different type.

If item 54 is set to 1, SPAG recognizes PDP style PARAMETER statements, but it treats them as entirely equivalent to standard Fortran PARAMETERs. This can lead to problems if SPAG is asked to insert declarations for IMPLICITly declared variables. For example, in the above case, SPAG might deduce, wrongly, that VDIM was REAL. The resulting output code will not compile. Currently, the only cure for this is to correct the inserted declarations, and convert the PDP style PARAMETERs to standard Fortran by hand.

Item 55

FUNCTION with no Arguments

Specifies whether FUNCTIONs may be declared with no argument list. Note that many compilers will misinterpret such FUNCTION statements.

      INTEGER FUNCTION random

may be interpreted as a declaration of an INTEGER variable

      INTEGER functionrandom

Possible values are:

0

Disallow FUNCTION statements without arguments.

1

Allow FUNCTION statements without arguments.

Item 56

INCLUDE file Search Rule (1)

Specifies the default search rule for INCLUDE files. This is relevant when SPAG is processing files which are not in the current directory and which INCLUDE files with no specified path. Possible values are:

0

INCLUDE files with no specified path are assumed to be in the current directory.

1

INCLUDE files with no specified path are assumed to be in the same directory as the source file.

For most compilers, a value of 0 should be specified. However, others, including Microsoft Fortran, require a value of 1.

If the INCLUDE file is not found using the above rule, the list of directories specified using item 215 is searched.

Item 59

SQL Statements

Specifies whether embedded EXEC SQL statements, as used with IBM's DB2 database are permitted. Possible values are:

0

EXEC SQL statements are not recognized.

1

EXEC SQL statements are recognized, and processed as follows:

  • The layout of the statements is preserved, but SPAG attempts to indent them to match surrounding statements. If, after indentation, the statement would extend beyond the right margin, SPAG reduces the indentation as necessary.
  • SPAG recognizes labels in the label field of EXEC SQL statements, and changes them as required. Statement labels may not appear within the body of an EXEC SQL statement.
  • SPAG parses the statement to determine which variables are modified and/or used and updates the symbol table accordingly.
  • SPAG converts the case of variable names which appear within an EXEC SQL statement using the controls in items 100 to 104.

Item 60

Reformat Declaration Statements

Specifies whether SPAG should preserve the format of all statements in the declaration section of each subprogram (i.e. before the first executable statement). Possible values are:

0

Reformat declaration statements. The same statements appear in the declaration section, but spacing and case will probably be different.

1

Do not reformat declaration statements. The declaration section is completely unchanged in the restructured code. This option is appropriate if some declaration statements have been carefully hand-formatted. An alternative approach is to use the !-ASIS directive to preserve the format of selected single declaration statements (see section 2.7.10).

Item 61

Tab Characters

Specifies whether SPAG should use tab characters in the output file. If this option is activated, SPAG replaces repeated space characters by tabs where possible. Possible values are:

0

Do not use tab characters.

n

Use tab characters where possible. There are assumed to be tab stops at columns n, 2*n, 3*n etc.

The use of tabs is an extension to ISO standard Fortran which is supported by most, but not all, Fortran compilers. SPAG never replaces tab characters which are within character constants.

Item 62

GOTO or GO TO

Specifies whether GOTO should be spelled with a space in the restructured output code. Possible values are:

0

for GOTO.

1

for GO TO.

Item 63

ENDIF or END IF

Specifies whether ELSEIF, ENDIF, ENDDO, ENDWHERE and ENDFORALL and other ENDXXX keywords should be spelled with a space in the restructured output code. Possible values are:

0

for ELSEIF, ENDIF, ENDDO but other END XXX keywords have a space.

1

for ELSE IF, END IF, END DO, and other END XXX keywords have a space.

2

No spaces in ELSEIF, or any END XXX keyword.

The second usage (with spaces) often causes confusion to programmers more familiar with Pascal or C than Fortran. Both these languages lack an ELSEIF statement, and, unlike Fortran, both treat spaces as significant syntactic elements. As a result, ELSE IF is often read as an ELSE statement followed by an IF statement. This leads to confusion both over the number of ENDIF statements required to terminate a block IF, and over the way the code should be indented. The Fortran ELSEIF statement is in fact more like the elsif statements in ADA and Modula 2.

Item 64

BLOCKDATA or BLOCK DATA

Specifies whether BLOCKDATA should be spelled with a space in the restructured output code. Possible values are:

0

for BLOCKDATA.

1

for BLOCK DATA.

Item 65

DOUBLEPRECISION or DOUBLE PRECISION

Specifies whether DOUBLEPRECISION should be spelled with a space in the restructured output code. Possible values are:

0

for DOUBLEPRECISION.

1

for DOUBLE PRECISION.

Item 66

Space after Fortran Keywords

Specifies whether a space is required after Fortran keywords. Possible values are:

0

No space.

      IF(a.GT.b)CALLsub

1

Space except where keyword is followed immediately by a bracket. (e.g. FORMAT, IF, OPEN).

      IF(a.GT.b)CALL sub 

With this setting, spaces are also inserted after the label in a DO statement, after the second '/' in a labelled COMMON statement and after the label in an ASSIGN statement.

      COMMON /abc/ a,b,c
      ASSIGN 22 TO xx

2

Always put a space after a keyword

      IF (a.GT.b) CALL sub

With this setting, spaces are also inserted after the closing ')' in an IF statement and after the ')' which terminates a bracketed control list in an I/O statement.

      IF (c.GT.d) THEN
      WRITE (*,*) a,b,c

Item 67

Spaces around + and -

Specifies whether a space is to be placed on either side of + or - operators. Possible values are:

0

No space either side of + or -.

n

Space either side of + or - if fewer than n brackets are open.

For example

      ! Item 67 = 0
            Y = X+(A*B-C)

      ! Item 67 = 1
            Y = X + (A*B-C)

      ! Item 67 = 2
            Y = X + (A*B - C)

SPAG does not place spaces around arithmetic operators (such as *, / and **) which bind more strongly than + and -. This ensures that spacing reinforces the Fortran standard precedence of arithmetic operators. For example, if SPAG were to produce

      ! Misleading use of spacing
            Y = X+(A * B-C)

it would appear that (B-C) was multiplied by A and the result added to X.

Item 68

Spaces around =

Specifies whether a space is to be placed on either side of = (mainly in assignment and DO statements). Possible values are:

0

No space either side of =.

n

Put spaces on either side of = if fewer than n brackets are open.

For example:

      ! Item 68 = 0
            Z=X*Y

      ! Item 68 = 1
            Z = X*Y

Item 69

Spaces around Logical Operators

Specifies whether a space is required on either side of logical operators (.AND., .OR., .EQV., .NEQV., .XOR. etc.). Possible values are:

0

No space either side of logical operators.

n

Spaces either side of logical operators if fewer than n brackets are open.

-n

Spaces either side of both relational and logical operators if fewer than n brackets are open.

For example

      ! Item 69 = 0
            QSW = b.GT.a.AND.b.LT.c

      !  Item 69 = 1
            QSW = b.GT.a .AND. b.LT.c

      !  Item 69 = -1
            QSW = b .GT. a .AND. b .LT. c

In Fortran, relational operators (such as .LT., .EQ. etc.) bind more strongly than logical operators (.AND., .OR. etc.), and a value of n>0 makes this precedence clearer.

Item 70

Spaces around Commas

Specifies whether a space is required on either side of commas. Possible values are:

0

No space either side of commas.

n

Spaces either side of commas if fewer than n brackets are open. If n is negative, a space is placed after the comma, but not before.

Item 71

Spaces in IF statement

Specifies whether a space should be inserted on either side of the test expression in IF statements. Possible values are:

0

No extra spaces.

1

Insert extra spaces.

For Example

      ! Item 71 = 0
            IF (Q1) THEN

      ! Item 71 = 1
            IF ( Q1 ) THEN

Item 73

Spaces around Concatenation Operator

Specifies whether a space is required on either side of the concatenation operator ('//').

Possible values are:

0

No space either side of //.

n

Spaces either side of concatenation operators if fewer than n brackets are open.

For example

      ! item 73 = 0
            zname = '*.'//zextn

      ! item 73 = 1
            zname = '*.' // zextn

Item 79

Separate Declaration for every variable

Specifies whether SPAG is to create a separate type declaration statement for every variable. This option is active only if item 4 is set greater than 2 (to invoke the declaration rewriting option). Possible values are:

0

Multiple variables on a single declaration.

            INTEGER i , j

1

One variable per declaration.

            INTEGER i
            INTEGER j

2

Similarly for SAVE, EXTERNAL and INTRINSIC statements.

Item 80

Preserve Statement Labels

Specifies whether statement labels are to be preserved. Possible values are:

0

Discard all pre-existing labels and start re-labelling from scratch.

n

Preserve labels with a value greater than or equal to n. Other labels are discarded. Labels which are redundant in the unscrambled code are removed.

-n

Preserve labels with a value greater than or equal to n, even if they are redundant in the unscrambled code. Some compilers object to this, particularly if the redundant label is on an ELSE or ELSEIF statement.

For example a value of -8000 would allow SPAG to discard all labels with a value of less than 8000, and where necessary, invent new ones according to the rules specified below. Labels with a value of 8000 or more are preserved in the unscrambled code, even if they are not referenced.

The most commonly used values are 0 (to discard all pre-existing labels) and 1 (to preserve all non-redundant labels). In some circumstances, for example when several DO loops terminate at the same statement, SPAG may have to invent new labels, whatever setting is used.

Item 81

New Label Base

Initial base counter for new labels (except those on FORMAT statements). When SPAG needs to generate a new label, it increments the base counter to the next higher multiple of the increment appropriate to the current indentation level (i.e. the number of open DO, IF or SELECT CASE blocks). For example, if the current base counter is 945, and a label is required on a statement which is indented 3 times, and if the increment for this degree of indentation is 20, then the label will be 960 (the next higher multiple of 20).

Item 82

Label Increment 0

Label increment for un-indented code - i.e. labelled statements which are not inside any DO or block IF constructs.

Item 83

Label Increment 1

Label increment for code at indentation level 1 - i.e. labelled statements inside only one DO or block IF construct.

Item 84

Label Increment 2

Label increment for code at indentation level 2.

Item 85

Label Increment 3

Label increment for code at indentation level 3.

Item 86

Label Increment 4

Label increment for code at indentation level 4.

Item 87

Label Increment 5

Label increment for code at indentation level 5 or above.

Item 88

Format Label Base

Initial base counter for re-labelling FORMAT statements. When SPAG needs to generate a new label for a FORMAT statement, it increments the FORMAT statement base counter to the next higher multiple of the FORMAT statement increment. For example, if the current FORMAT statement base counter is 99010, and the FORMAT statement increment is 2, then the next FORMAT statement will have the label 99012.

Item 89

Format Label Increment

FORMAT statement label increment to be used in conjunction with the FORMAT statement base counter as described above.

If the FORMAT statement increment is set to 0, FORMAT statement labels are not treated specially, but are calculated using the base counter and increments defined in items 81 to 87.

Item 90

Label Justification

Specifies how statement labels are to be formatted. Possible values are:

0

Right justified at column 5.

n

Left justified at column n.

In fixed format code, a value of 2 (left justified at column 2) has the advantage of separating labels visually both from comments (C or * in column 1) and continuation markers (column 6).

SPAG will over-ride the specified rule if the label will not otherwise fit. For example if the label is 99001, it will start in column 1 regardless of how this control is set.

Item 100

Case of Local Variables

Specifies what case conversion is done for local variable names. Possible values are:

-1

Leave case unchanged.

0

Convert to lower case.

n

(n<9) Convert first n characters to upper case, and the remainder to lower case.

9

Convert to upper case.

If SPAG has to insert new occurrences of the symbol (for example, when inserting declarations for implicitly typed variables), it will use upper case if the corresponding control is set to -1.

The use of lower case in variable names is not strictly standard Fortran 77, though all recent compilers accept it.

Item 101

Case of COMMON variables

Specifies what case conversion is done for COMMON variable names. Possible values are as detailed for item 100.

Item 102

Case of Dummy Arguments

Specifies what case conversion is done for dummy argument names. Possible values are as detailed for item 100.

Item 103

Case of PARAMETERs

Specifies what case conversion is done for PARAMETER names. Possible values are as detailed for item 100.

Item 104

Case of other Symbols

Specifies what case conversion is done for other user-defined symbols (COMMON block names, NAMELIST names, subprogram names etc.). If item 2 is set to -1 (no symbol tables generated), then this control applies to all user-defined symbols. Possible values are as detailed for item 100.

Item 106

Case of logical and relational operators

Specifies what case conversion is done for logical and relational operators (.AND., .NE. etc.). Possible values are:

0

Convert logical and relational operators to lower case.

1

Convert logical and relational operators to upper case.

Item 107

Case of Fortran keywords

Specifies what case conversion is done for Fortran keywords (READ, THEN, INTEGER etc.). Possible values are:

0

Convert Fortran keywords to lower case.

1

Convert Fortran keywords to upper case.

An unusual feature of Fortran is that it does not have reserved names. Thus 'INTEGER' is a perfectly valid variable name, even though it is also a Fortran keyword. SPAG recognizes this distinction, and uses item 107 only if the context confirms that a word is used as a keyword.

Item 108

Case of Character Strings

Specifies what case conversion is done for character strings. Possible values are:

0

Leave the case of character strings unchanged (recommended).

-1

Convert character strings to lower case.

1

Convert character strings to upper case.

Item 109

Case of Comments

Specifies what case conversion is done for comments (from column 2 onwards). Possible values are:

0

Leave the case of comments unchanged.

-1

Convert comments to lower case.

1

Convert comments to upper case.

Item 121

Right Margin for Output

The right margin for source code - normally set to 72 for fixed format code. SPAG ensures that source code is not written past the right margin; if necessary, SPAG inserts continuation lines to hold the extra code. For conformance to the Fortran 77 standard, set the right margin to 72 or less. Most Fortran 77 compilers ignore any text after column 72, though some can be configured to accept it. The maximum permitted value for the right margin is 5120.

Comments are NOT truncated at the right margin.

Item 122

Left Margin for Output

The left margin for un-indented statements (excluding labels). Fortran 77 reserves the first six columns of each record for labels and continuation markers, so the value specified here must be at least 7, unless the new Fortran 95 source form is used (see item 141).

Item 123

Unit of Indentation

The number of characters by which statements within active DO loop, block IF or SELECT CASE constructs are to be indented. For example if a value of 3 is specified, and un-indented statements start at column 7 (item 122) then statements which are inside 4 open block constructs will be started at column 19 (7 + 4x3).

Item 124

Maximum Indentation

The maximum permitted left margin (after indentation). This item places a limit on the amount of indentation. Without it, programs with a lot of open blocks could be placed beyond the right margin.

Item 125

Continuation Character

The character code of the continuation character. SPAG inserts CHAR(n) at column 6 of all continuation lines of fixed format source code. Use of the '&' character is recommended for compatibility with Fortran 95. If you specify that output is to be written using the free format Fortran 95 source form (see item 141), '&' is used as the continuation character, and this control has no effect. The character code for '&' is 38 on ASCII based machines.

Item 126

Comment Character

The character code of the comment character. SPAG inserts CHAR(n) at column 1 of all comment lines (excluding lines with end of line comments).

For fixed form source code, suitable ASCII values for n are 33 (!), 42 (*), 67 (C) and 99(c). If the item is set to 0, the pre-existing comment character is retained.

If you specify that output is to be written using the free format Fortran 95 source form (see item 141), '!' is used as the continuation character, and this control has no effect.

Item 127

Comment Justification

Specifies whether SPAG should reformat the text of comments. Possible values are:

0

Do not reformat comments

n

Left justify the comment text to column n. The comment character in column 1 is not moved. SPAG attempts to preserve existing indentations within comment blocks.

81

Comments are aligned with the surrounding code, and text is word-wrapped so that comments do not extend beyond column 80. Comments before an IMPLICIT statement are not changed. Set this control to 82 or 83 causes other comments within the declaration section to be preserved.

Item 130

COMMON Block Alignment Tests

Specifies whether SPAG should check the alignment of variables and mixed CHARACTER/non-CHARACTER variables in COMMON blocks. Possible values are:

0

No COMMON block alignment checks.

1

SPAG reports cases where the offset of a COMMON variable from the start of a COMMON block is not a multiple of the variable element size. For example, if an 8 byte variable is offset 36 bytes from the start of a COMMON block, SPAG issues a warning, because 36 is not a multiple of 8.

SPAG also warns if CHARACTER and non-CHARACTER items are mixed in a COMMON block. This is not allowed by the ISO standard, and could lead to alignment problems.

Item 131

Floating Point Equality Test

Specifies whether SPAG should report the existence of unsafe tests for equality of floating point data. Possible values are:

0

No checks for floating point equality tests.

1

SPAG reports cases where a relational expression depends on a test for equality (or inequality) between REAL or DOUBLE PRECISION data. Such tests may produce irreproducible results, because floating point numbers are not represented exactly.

Item 132

Variable Name Checks

Specifies whether SPAG should issue a warning if a variable or array name is the same as a Fortran keyword or INTRINSIC function. Possible values are:

0

No checks on variable names.

1

SPAG issues a warning if any variable or array name is the same as a Fortran keyword or INTRINSIC function. The list of Fortran keywords includes Fortran 77 and common extensions.

2

As above but including Fortran 90/95 keywords.

3

As above but including Fortran 2003 keywords.

Item 133

Subprogram Argument Checks

Specifies whether SPAG should check for consistency of subprogram arguments. If this option is selected, SPAG warns if the number or type of arguments varies between invocations of a subprogram. This is appropriate for Fortran 77 source code, but in Fortran 95 the number of arguments may vary if the callee has optional arguments, and the type may vary if the callee is overloaded. For this reason, it is recommended that this check be switched off when processing Fortran 95 input code. Possible values are:

0

No checks on subprogram argument consistency (recommended for Fortran 95 input code).

1

SPAG issues a warning if the number and/or type of arguments varies between invocations of a subprogram.

Item 134

Unused Variable Checks

Specifies whether SPAG should check for variables, PARAMETERs and dummy arguments which are declared but never used. This control operates only if Clutter Checks are active. Possible values are:

0

No checks on unused variables.

1

SPAG issues a warning if a local variable, PARAMETER or dummy argument is declared, but never set or used.

2

In addition, SPAG issues a warning if a local variable, is declared, and assigned a value but never used.

Item 135

Used but not Set Checks

Specifies whether SPAG should check for local variables whose value is undefined when used. This is a serious error, which can lead to unpredictable run-time behaviour. However this test can only catch a relatively small subset of the errors. A much more thorough check can be performed using the SPAG's Dynamic Analysis facility. This control operates only if Clutter Checks are active. Possible values are:

0

No checks for used but not set.

1

SPAG issues a warning if it is sure that a local variable is not set when used. Not all such errors will be flagged.

Item 136

Unused COMMON blocks and INCLUDE files

Specifies whether SPAG should check for COMMON blocks and INCLUDE files which are entirely unused in the current subprogram. This control operates only if Clutter Checks are active. Possible values are:

0

No checks on unused COMMON blocks and INCLUDE files.

1

SPAG issues a warning if a COMMON block or INCLUDE files is unused in the current subprogram.

Item 138

Conversion of #include to INCLUDE

Specifies whether SPAG should convert C preoprocessor includes (using #include) to standard Fortran 95 format (using an INCLUDE statement). Possible values are:

0

Convert #include to a standard Fortran INCLUDE statement.

1

Leave #include unchanged.

Item 140

Input Source Form

Specifies the format of the input source code. Possible values are:

0

the input source form is inferred from the file extension. Thus, .for and .f files are assumed to be fixed format (F77 source form), whereas .f90 and .f95 are assumed to be free format (f95 source form). In the case of fixed format files, SPAG also distinguishes strictly fixed format files, which may have sequence numbers in columns 73-80, from VAX style tabbed format files, which may use tabs for indenting, and have source code extending beyond column 72. Continued character constants are also treated differently in the VAX style source form. For example, given:

     DATA zalph/'ABCDEFGHIJKLM
    &NOPQRSTUVWXYZ'/

in the FORTRAN 77 fixed form, the first line is padded with spaces to column 72, whereas in the VAX source form, there is no automatic padding, and, depending on the position of the end-of-line marker (carriage return) on the first line, there may be no spaces between M and N.

1

input code is free format (F95 source form).

2

input code is fixed format (F77 source form).

3

input code is VAX tabbed format.

Item 141

Output Source Form

Specifies the required format of the output source code. Possible values are:

0

output code is fixed format (F77 source form).

1

output code is free format (F95 source form). Suprogram, CONTAINS and END statements are outdented relative to declarations.

2

output code is F77 and F95 compatible. The output code is in the Fortran 95 free format source form, but is compatible with the fixed Fortran 77 source form, provided that the compiler recognizes lines beginning with '!' as comments.

3

output code is free format (F95 source form). Suprogram, CONTAINS and END statements are outdented and executable code is indented relative to declarations.

If this control is set greater than 0, item 125 (the continuation character) is set to '&' and item 126 (the comment character) is set to '!'. In addition the first characters of items 220-223 (various comments inserted by SPAG) are changed to '!'.

If this control is set to 0 or 2, and item 122 (the left margin for output code) is less than 7, then item 122 is set to 7.

Item 210

File containing Intrinsic Function Names

The name of a file containing a list of INTRINSIC and 'safe' functions. The file is plain text and consists of a list of function names separated by spaces and/or new lines. Four sample files containing lists of intrinsic functions in standard Fortran 2008 (f2008func.txt), Fortran 95 (f95func.txt), Fortran 77 (f77func.txt) and VAX Fortran (vaxfunc.txt) are supplied with SPAG. If the file contains a '/' character on a line by itself, then names before it are interpreted as intrinsic functions, and names after it are interpreted as non-intrinsic safe functions (see item 12).

Item 211

File containing Renames

The name of a file containing a list of renames for the current run. This file is read only if item 5 is set to 1 or more. See section 2.7.9 for further discussion.

Item 213

File for Translation INTEGER*2 etc.

The name of a Fortran 95 source file containing a skeleton implementation of the standard FORTRAN module ISO_FORTRAN_ENV. This file is normally in the plusFORT installation directory. The module contains definitions of KIND types required to convert non-standard Fortran types, such as INTEGER*2 and REAL*4 to standard Fortran 95 types. For example, INTEGER*2 is normally translated as INTEGER(INT16) where INT16 is a PARAMETER defined in ISO_FORTRAN_ENV.

SPAG inserts a "USE ISO_FORTRAN_ENV" at the head of processed code that needs it, and the compiler will then use its own implementation.

Item 215

INCLUDE file Search Path (2)

A list of directories, separated by semicolons, which are to be searched if an INCLUDE file which is specified without a path is not found in the current directory (or the source directory - see item 56). For example, if, on a Windows system the list is C:\INCLUDE;C:\SYSIN, and if an included file name does not specify a path, SPAG looks first in the current directory (but see item 56), then in C:\INCLUDE and finally in C:\SYSIN. If it is not found in any of these directories, SPAG reports an error. For Linux and Mac systems, the file naming conventions are different, but otherwise the mechanism is the same.

The configuration file may contain multiple 215= lines each with a list of INCLUDE directories. If it does, the effect is that the lists are concatenated. This option is useful in complex builds requiring a long list of INCLUDEs. Note that individual lines in the configuration file may not be more than 256 characters long.

Item 220

Header Record Identifier

Four characters used as header record identifiers in the output file. SPAG writes a header record before each subprogram in the output file. The header record consists of a 4 character identifier, followed by a file name. QSPLIT uses these file names to split output files created by SPAG into a single file for each subprogram. The default header record identifier is !*==. Standard Fortran 77 does not recognise lines beginning with ! as comments, though, in practice, most recent compilers do. If your compiler does not recognise ! as a comment character, it will be necessary to change the header record identifier (e.g. to **==). If the header record identifier is blank, no header records are written.

For example:

      !*==TEST1.NEW

It is very important to ensure that the header record identifier is not used in any other context, as SPAG removes pre-existing header records when it writes the output file. For example, it would be very unwise to use the character 'C' as the header record identifier, as SPAG would then delete any line which begins with a 'C' followed by three spaces.

Item 221

Interface Description Header Marker

When GXCHK inserts an Interface Description block into the restructured source code (see Section 3.6), it inserts header and trailer marker comments. This is done so that it can remove any pre-existing interface description block before inserting a new one. This item specifies the four characters at the beginning of the header marker. The default is !*--. If your compiler does not recognise ! as a comment character, it will be necessary to change this item (e.g. to **--).

As with item 220, it is very important to ensure that the marker is not used in any other context, or GXCHK might then remove comments which are not part of an old interface description block.

Item 222

Interface Description Trailer Marker

Specifies the four characters at the beginning of the Interface Description Trailer Marker (see item 221). The default is !*++. If your compiler does not recognise ! as a comment character, it will be necessary to change this item (e.g. to **++).

Item 223

Marker for First Executable Statement

If this item is set, it is treated as a comment to be inserted before the first executable statement in every subprogram. For example:

      !***_Start_of_Executable_Program

Before output, SPAG translates any underscore characters in the value to spaces.

Item 229

Directory for SPAG output files (new in version 7.10)

In version 7.10 and later, SPAG output files (restructured output, symbol files, coverage files, and module files) are written into a subdirectory whose name is specifed in item 229. The default directory name is SPAGged, and SPAG creates the directory if it does not exist. Restructured output files have the extension .f90 (or .for if item 141 is set to 0) instead of .spg. If item 229 is present in spag.fig, items 230, 231, 232, 234 and 236 are over-ridden.

The original (pre 7.10) behaviour is restored if item 229 is absent from spag.fig.

Item 230

Output File Directory (or prefix)

Specifies a prefix for the names of restructured output files. This could be a directory name (the directory must exist), or simply a prefix to the filename. A closing '/' or '\' is required (depending on operating system conventions) if a directory is specified.

If no prefix is specified, restructured source files are written in the same directory as the input source code.

Item 231

Output File Extension

The filename extension to be used for restructured output files (e.g. .spg). The extension is also used as the filename extension on header records in the output file (see item 220). SPAG constructs the file name on the header records by appending the extension specified here to the subprogram name.

Item 232

Symbol File Directory (or prefix)

Specifies a prefix for the names of symbol table files. This could be a directory name (the directory must exist), or simply a prefix to the filename. A closing '/' or '\' is required (depending on operating system conventions) is required if a directory is specified.

If no prefix is specified, symbol table files are written in the same directory as the input source code.

Item 233

Symbol File Extension

The filename extension to be used for symbol table files (e.g. .smb ).

Item 234

Coverage Data File Directory (or prefix)

Specifies a prefix for the names of coverage data files. This could be a directory name (the directory must exist), or simply a prefix to the filename. A closing '/' or '\' is required (depending on operating system conventions) is required if a directory is specified. It is strongly recommended that an absolute path rather than a relative path be specified (e.g. C:\myprog\cover\ rather than ..\cover\), as this will allow the instrumented program to find the coverage data files in all cases.

This item must be specified if item 6 is set to 2.

Item 235

Coverage Data File Extension

The filename extension to be used for coverage data files. This is normally set to .cvr.

Item 236

Module File Directory (or prefix)

Specifies a prefix for the names of module files created by SPAG (includes INTERFACE modules, and modules to replace COMMON blocks and INCLUDE files). This could be a directory name (the directory must exist), or simply a prefix to the filename. A closing '/' or '\' is required (depending on operating system conventions) is required if a directory is specified.

Item 237

Module File Extension

The filename extension (e.g. .ifc or .f90) to be used for the output files containing the source code for modules created by SPAG (includes INTERFACE modules, and modules to replace COMMON blocks and INCLUDE files).

Item 238

Prefix for Names and Labels invented by SPAG

A short prefix, such as "SPAG_", used as a prefix to names and labels created by SPAG to ensure that the invented names are unique. If you use SPAG to process code that was previously SPAGged (using V8 or later), you may need to set a different prefix for the second run.

Item 240

Dynamic Analysis Probe Routine Root 1

A 3 character root used at the start of the names of the dynamic analysis probe routines which check whether variables are defined or not. The three characters should be chosen so as to avoid clashes with other routines. If you change this item from the default value (QD_), you will also have to change the source for the probe routines (in the file probes.f90). See section 2.8 for further discussion of dynamic analysis.

Item 241

Dynamic Analysis Probe Routine Root 2

A 3 character root used at the start of the names of additional dynamic analysis probe routines. The three characters should be chosen so as to avoid clashes with other routines. Currently, this item is not used.

Item 242

Dynamic Analysis Undefine Routine Root

A 3 character root used at the start of the names of the dynamic analysis probe routines which set variables to the undefined value. The three characters should be chosen so as to avoid clashes with other routines. If you change this item from the default value (UD_), you will also have to change the source for the probe routines (in the file probes.f90). See section 2.8 for further discussion of dynamic analysis.

Item 243

Program Entry Trace Routine

If item 6 is set greater than 0, a call is inserted in the output code so that it is executed immediately after the program starts. The name of the routine is formed by concatenating the prefix with the root defined in here. It should not normally be changed.

See section 2.9 for further discussion of coverage analysis.

Item 244

Program Exit Trace Routine

If item 6 is set greater than 0, a call is inserted in the output code so that it is executed immediately before the program stops. The name of the routine is formed by concatenating the prefix with the root defined in here. It should not normally be changed.

See section 2.9 for further discussion of coverage analysis.

Item 245

Subprogram Entry Trace Routine

If item 6 is set greater than 0, a call is inserted in the output code so that it is executed whenever a subprogram is invoked. The name of the routine is formed by concatenating the prefix with the root defined in here. It should not normally be changed.

See section 2.9 for further discussion of coverage analysis.

Item 246

Subprogram Exit Trace Routine

If item 6 is set greater than 0, a call is inserted in the output code so that it is executed whenever control is returned from a subprogram. The name of the routine is formed by concatenating the prefix with the root defined in here. It should not normally be changed.

See section 2.9 for further discussion of coverage analysis.

Item 247

Code Block Entry Trace Routine

If item 6 is set greater than 0, a call is inserted in the output code so that it is executed whenever a code block is entered. The name of the routine is formed by concatenating the prefix with the root defined in here. It should not normally be changed.

See section 2.9 for further discussion of coverage analysis.

2.7 Using SPAG

2.7.1 Control Flow Restructuring

One of the primary functions of SPAG, the one that gives it its name, is the restructuring of control flow within subprograms. Obsolete constructs, such as GOTO, arithmetic IF and computed GOTO are replaced by block IFs, DO loops with EXIT and CYCLE, and SELECT CASE constructs. When restructuring is turned on (by setting item 1 to 3), SPAG's default settings are generally sufficient for most inputs. However the output can be tuned in various ways:

SPAG does not currently restructure some structures which cause a GOTO-like jump. These are:

2.7.2 Adding Declarations

Most older Fortran programs are written in an 'implicitly typed' environment. That is to say that variables are not formally declared at the head of each procedure as they are in Pascal or C; instead, variables are implicitly declared simply by using them. The compiler determines the type of the variable from the first letter of its name. For example, variables whose name begins with the letter 'I' are normally of type INTEGER.

No doubt this appeared to the pioneers who created Fortran as an advantage, and at first sight it may appear that the compiler is simply doing automatically what the programmer would otherwise have to do by hand. We now know that it is all too easy for the compiler to create variables in a way not intended by the programmer. A simple example illustrates the point:

        I0 = 0
        DO 10 i = 1.100
           READ *,j
           IO = I0 + j
   10   CONTINUE

In this example, the variable I0 is mis-typed as IO on line 4, and as a result a spurious new variable IO is created. Moreover, the second statement is, despite appearances, an assignment to the spurious REAL variable DO10I (spaces are not significant in Fortran). A language which requires explicit declarations would have spotted that IO and DO10I had not been declared, and reported an error at compile-time. But mis-spelling is not the only problem. Even commoner is confusion between local and COMMON variables. For example, if you INCLUDE a file which declares a COMMON variable named K, in a routine which has a local variable called K, all manner of obscure symptoms are possible.

Fortran can be made to require explicit typing. The IMPLICIT NONE statement, which disables implicit typing, is standard Fortran 95, but even Fortran 77 compilers generally accept it. Its use can be thoroughly recommended. As an alternative, some compilers have a compiler switch to disable implicit typing without resort to IMPLICIT NONE.

SPAG provides the means for converting existing, implicitly typed programs to explicitly typed programs for use with IMPLICIT NONE. It does this by adding explicit declarations for all undeclared variables at the head of each subprogram and INCLUDE file. Doing this by hand on a large program could take weeks - enough to make the switch impractical. However, the fact that SPAG can do it quickly doesn't mean it should be done carelessly; for example, there would be little point in doing it as a regular routine, as the benefits of explicit typing are then lost. Ideally, the declarations inserted by SPAG should be checked for spurious entries caused by past mistakes. However, explicit typing will help prevent future errors even if some past errors remain.

The 'auto-declarer' feature of SPAG is activated by setting item 4 of spag.fig greater than 0. If it is set to 1, declarations are inserted for all undeclared variables, parameters, dummy arguments and functions other than intrinsic functions (whose names are read from a file - see item 210 of spag.fig). Intrinsic functions are excluded because their type may vary (e.g. MIN may be REAL in one place and INTEGER in another). If item 4 of spag.fig is set to 2, SPAG also inserts an IMPLICIT NONE statement, and removes any pre-existing IMPLICITs (provided that they are not in an INCLUDEd file).

For example, given the following code:

      SUBROUTINE STATE(U1,U2,U3,U4,U,V,Rho,P,E,T,Mx,My)
      IMPLICIT REAL*8(A-H,O-Z)
      PARAMETER (NX=150,NY=150)
      DIMENSION U(NX,NY) , Rho(NX,NY) , V(NX,NY) , E(NX,NY)
      DIMENSION P(NX,NY) , U1(NX,NY) , U2(NX,NY) , U3(NX,NY) ! comment
      DIMENSION U4(NX,NY) , T(NX,NY)
      gma = DBLE(14)/DBLE(10)
      one = DBLE(1)
      p5 = DBLE(5)/DBLE(10)
      gcn = (gma-one)
      rr = 287.0
      DO i = 1 , Mx
         DO j = 1 , My
            Rho(i,j) = U1(i,j)
            E(i,j) = U4(i,j)/U1(i,j)
            U(i,j) = U2(i,j)/U1(i,j)
            V(i,j) = U3(i,j)/U1(i,j)
            P(i,j) = gcn*Rho(i,j)                                       &
     &               *(E(i,j)-p5*(V(i,j)*V(i,j)+U(i,j)*U(i,j)))
            T(i,j) = P(i,j)/(Rho(i,j)*rr)
         ENDDO
      ENDDO
      CALL SUB2(U4)
      CONTINUE
      END

then if item 4 is set to 2, SPAG produces:

SUBROUTINE state(U1,U2,U3,U4,U,V,Rho,P,E,T,Mx,My)
   IMPLICIT NONE
!*** Start of declarations inserted by SPAG
   REAL*8 E , gcn , gma , one , P , p5 , Rho , rr , T , U , U1 , U2 , U3 , U4 , V
   INTEGER i , j , Mx , My , NX , NY
!*** End of declarations inserted by SPAG
   PARAMETER (NX=150,NY=150)
   DIMENSION U(NX,NY) , Rho(NX,NY) , V(NX,NY) , E(NX,NY)
   DIMENSION P(NX,NY) , U1(NX,NY) , U2(NX,NY) , U3(NX,NY)    ! comment
   DIMENSION U4(NX,NY) , T(NX,NY)
   gma = dble(14)/dble(10)
   one = dble(1)
   p5 = dble(5)/dble(10)
   gcn = (gma-one)
   rr = 287.0
   DO i = 1 , Mx
      DO j = 1 , My
         Rho(i,j) = U1(i,j)
         E(i,j) = U4(i,j)/U1(i,j)
         U(i,j) = U2(i,j)/U1(i,j)
         V(i,j) = U3(i,j)/U1(i,j)
         P(i,j) = gcn*Rho(i,j)*(E(i,j)-p5*(V(i,j)*V(i,j)+U(i,j)*U(i,j)))
         T(i,j) = P(i,j)/(Rho(i,j)*rr)
      ENDDO
   ENDDO
   CALL sub2(U4)
END SUBROUTINE state

2.7.3 Rewriting Declarations

SPAG can also rewrite the declaration section of a subprogram from scratch, using either Fortran 77 or Fortran 95 declaration style. This is done by setting item 4 of the configuration data to 4 or 6 respectively.

For the example above, if item 4 is set to 6 and item 16 to 1, SPAG produces:

SUBROUTINE state(U1,U2,U3,U4,U,V,Rho,P,E,T,Mx,My)
   USE ISO_FORTRAN_ENV
   IMPLICIT NONE
!
! PARAMETER definitions rewritten by SPAG
!
   INTEGER , PARAMETER :: NX = 150 , NY = 150
!
! Dummy argument declarations rewritten by SPAG
!
   REAL(REAL64) , INTENT(IN) , DIMENSION(NX,NY) :: U1
   REAL(REAL64) , INTENT(IN) , DIMENSION(NX,NY) :: U2
   REAL(REAL64) , INTENT(IN) , DIMENSION(NX,NY) :: U3
   REAL(REAL64) , DIMENSION(NX,NY) :: U4
   REAL(REAL64) , INTENT(INOUT) , DIMENSION(NX,NY) :: U
   REAL(REAL64) , INTENT(INOUT) , DIMENSION(NX,NY) :: V
   REAL(REAL64) , INTENT(INOUT) , DIMENSION(NX,NY) :: Rho
   REAL(REAL64) , INTENT(INOUT) , DIMENSION(NX,NY) :: P
   REAL(REAL64) , INTENT(INOUT) , DIMENSION(NX,NY) :: E
   REAL(REAL64) , INTENT(OUT) , DIMENSION(NX,NY) :: T
   INTEGER , INTENT(IN) :: Mx
   INTEGER , INTENT(IN) :: My
!
! Local variable declarations rewritten by SPAG
!
   REAL(REAL64) :: gcn , gma , one , p5 , rr
   INTEGER :: i , j
   EXTERNAL sub2
!
! End of declarations rewritten by SPAG
!
   gma = dble(14)/dble(10)                                   ! comment
   one = dble(1)
   p5 = dble(5)/dble(10)
   gcn = (gma-one)
   rr = 287.0
   DO i = 1 , Mx
      DO j = 1 , My
         Rho(i,j) = U1(i,j)
         E(i,j) = U4(i,j)/U1(i,j)
         U(i,j) = U2(i,j)/U1(i,j)
         V(i,j) = U3(i,j)/U1(i,j)
         P(i,j) = gcn*Rho(i,j)*(E(i,j)-p5*(V(i,j)*V(i,j)+U(i,j)*U(i,j)))
         T(i,j) = P(i,j)/(Rho(i,j)*rr)
      ENDDO
   ENDDO
   CALL sub2(U4)
END SUBROUTINE state

SPAG breaks the declarations into four labelled sections, any of which may be absent:

Because of the fundamental nature of the rewriting process, it is not possible to retain comments in their original relation to declarations. Thus, in the above example, comments which were attached to the old declarations are grouped together after the rewritten code.

The original code uses REAL*8 which is a common extension, but not ISO standard Fortran. SPAG has converted the code to standard FORTRAN using the kind definitions from the standard ISO_FORTRAN_ENV module.

SPAG cannot rewrite declarations containing some Fortran 2003 features, such as abstract interfaces and bound procedures. SPAG issues a warning message, and suppresses rewriting if this situation occurs.

INCLUDE statements from the original code are, depending on item 14 of spag.fig, rewritten as modules, or placed before the first rewritten declaration. As a result:

2.7.4 Converting INCLUDE files to MODULEs "on the fly"

SPAG can convert INCLUDE files containing COMMON blocks and associated declarations into modern Fortran modules on the fly - as it processes the corresponding INCLUDE statement. If item 14 of spag.fig is set to 1, SPAG automatically converts INCLUDE statements to USE statements. This option should not be used if INCLUDE files contain other code, such as executable code, or local variable declarations, or INTERFACE declarations.

On the first occurrence of an INCLUDE file SPAG creates a file containing the corresponding MODULE. The INCLUDE files should not be explicitly listed as input files for SPAG - they are discovered because of their presence in the INCLUDE statement.

SPAG derives the module name from the INCLUDE file name by discarding the directory and extension, and prefacing the two characters i_. For example, an INCLUDE file called f:\include\abc.inc will be translated to a module called i_abc. This module is written to a file called \moddir\i_abc.xyz, where \moddir\ is the directory specified in item 229 (or 236), and .xyz is the extension specified in item 237.

To be translated correctly, the INCLUDE file must contain only INCLUDE, PARAMETER, COMMON and EQUIVALENCE statements, together with associated type and dimension statements. There should be no EQUIVALENCE statements outside the INCLUDE file that refer to its contents. SPAG will issue an error message if these rules are violated.

An advantage of this method is that SPAG sees the full context, avoiding the ambiguity that can arise when an INCLUDE file is processed in isolation. A problem is that, in the current implementation, comments from the INCLUDE file are not transferred to the MODULE.

2.7.5 Converting isolated INCLUDE files

SPAG can also process INCLUDE files in isolation. When SPAG encounters an input with no subprogram or END statement, it assumes it is an INCLUDE file, and converts it to a MODULE, by adding or rewriting declarations as specified in item 4 of spag.fig, and adding MODULE and END MODULE statements. Each INCLUDE file should be processed in a separate run. In order for this to work correctly, it is necessary to ensure that INCLUDE file contains sufficient information to define the type and nature of every symbol in it.

For example, if an INCLUDE file contains the single statement

       COMMON /ABC/  XX(N),I,J,K

we cannot determine the type of any of the variables. We know that XX is an array, but I, J and K may be too. We can infer that N is an INTEGER PARAMETER, but we don't know its value, or whether it is a 1, 2 or 4 byte INTEGER. It is even possible that the same INCLUDE file means different things in different places.

In cases like this, it is best to insert extra statements to fill in the missing information. For example, if you insert the two lines:

        IMPLICIT LOGICAL(A-J,O-Z),INTEGER*2 (K-N)
        PARAMETER (N=200)

then SPAG will be able to do a much better job. You should remove the redundant IMPLICIT statement when SPAG has finished (we recommend that IMPLICIT statements should never be placed in INCLUDE files).

The objective is to ensure that before processing, each INCLUDE file is self-contained, and not reliant on context to define its meaning. In fact this is a good principle in general: if all INCLUDE files are constructed this way, many subtle bugs are eliminated (e.g. the size of XX varies because there are multiple non-identical definitions of N).

A disadvantage of this method is that the user must also convert the INCLUDE statements to USE statements

SPAG currently has no mechanism for converting BLOCKDATA segments which initialize COMMON variables to a form which is compatible with MODULEs.

2.7.6 COMMON block Modules

SPAG can also translate COMMON blocks to MODULEs on the fly - as it processes the host subroutine. This option is activated by setting item 13 of spag.fig greater than 0. However it is strongly recommended that this should not be done unless you have previously checked by hand that every instance of a COMMON block is identical. In particular:

SPAG may not warn if these conditions are not met, and may produce invalid code as a result.

Despite these limitations, this facility can save a lot of work if the conditions are met, by automatically separating and removing declarations associated with COMMON blocks from the host subprograms.

SPAG derives the module name from the COMMON block name by discarding the '/'s, and prefacing the two characters c_. For example, a COMMON block called /HELLO/ will be translated to a module called c_hello. This module is written to a file called \moddir\c_hello.xyz, where \moddir\ is the directory specified in item 236, and .xyz is the extension specified in item 237.

2.7.7 Interface Modules

If item 15 of spag.fig is set to 1, SPAG creates modules containing interfaces for every subroutine or function it processes. It also inserts appropriate USE statements at the head of the calling subprograms. Together, these changes make it possible for a Fortran 95 compiler to check that the arguments in every subprogram reference match the corresponding dummy arguments.

At first sight, this may seem a pointless exercise, as plusFORT manages the same checks, and more, without any help from the user! However the modules can also be passed to others, and this will allow them to check interfaces without having access to the source code.

If the called subprogram name is abc, the interface module name will be s_abc, and it will be stored in a file as specified by items 11, 236 and 237 of spag.fig.

If the program calls a subprogram that has not been processed by SPAG, there is no interface module to satisfy the reference in the calling subprogram's USE statement. In this case, the user must either provide an interface module to satisfy the reference, or remove the USE statement.

The facilities described in this and the following two sections may be used as a prelude to a more thorough reorganization along modular lines. Section 3.7 describes facilities offered by plusFORT to assist ib a high-level restructuring exercise of that type.

2.7.8 Removing Clutter

As programs are developed, variables, parameters, COMMON blocks and code fragments fall into disuse. Programmers are often reluctant to remove such 'clutter' because of the possibility of un-looked for side effects. However over a period, clutter can make programs more obscure and difficult to maintain than they need be. SPAG can remove many types of clutter, and identify many others. Specifically, SPAG can remove the following (provided they are not defined in an INCLUDEd file):

If item 3 is set to 1, and item 29 to 0, SPAG identifies, but does not remove the above.

ISO standard Fortran allows compilers to allocate memory for COMMON blocks dynamically. Theoretically, a COMMON block becomes undefined if it is not referenced by any executing subprogram. In practice, most, if not all current compilers treat COMMON blocks as static, and many users would be confused and dismayed if they did not!

If SPAG removes an unreferenced COMMON block, the point at which the COMMON theoretically becomes undefined may also be altered. If you wish to avoid this possibility, specify the COMMON block name in a SAVE statement, set item 3 of the SPAG configuration data to 3 or less, or ensure that all COMMONs are specified in the main PROGRAM (SPAG never removes COMMONs or INCLUDEs from a main program or BLOCK DATA).

SPAG identifies, but cannot remove

The GXCHK program which uses the symbol tables written by SPAG to construct a global symbol table for an entire program identifies clutter on a program-wide basis (e.g. globally unused COMMON variables).

2.7.9 Renaming Symbols

SPAG provides a simple and safe method for systematically changing the names of symbols within a program. SPAG guards against the danger of choosing a new name which is already in use, and re-formats the source code appropriately. The rename facility is activated by setting item 5 of spag.fig to 1, and specifying the name of a file containing details of the required changes in item 211.

A sample rename file is shown below. The file consists of a series of sections, separated by blank lines. The first line of each section specifies the scope of the following instructions. The remaining lines in the section specify the before and after names, separated by spaces, of the symbol to be renamed. If the scope is '*', the changes are applied without restriction. This could be used, for example, to change specific function names, such as AMAX0, to the generic form (MAX). If the scope is a file-name surrounded by apostrophes, the changes are made only if the specified file is INCLUDEd in the current subprogram. Note that the INCLUDE file itself is not changed - only the references to it in the current subprogram. If the scope is a subprogram name, the changes are made only within the specified subprogram.

Example Rename File

   *
    AMOD10         mod
    AMIN1          min

    'test1.prv'
    fchsta         firstc
    zsyst          zzzsys
    /pkchAR/       /pkcomm/

    swstx2
    ityp           jtyp
    jtyp           ityp
    lopbr          lclbr
    lclbr          lopbr

    swfrag
    oxo            xox

2.7.10 The 'ASIS' Directive

In some circumstances, it may be desirable to suppress the re-formatting of statements. This is particularly true when a long declaration has been carefully aligned by hand to give a particular visual effect. SPAG allows you to suppress re-formatting by inserting a directive of the form

      !-ASIS

in the source code. This directive governs only the immediately following statement. Compilers treat the directive as a comment. C-ASIS and *-ASIS are accepted as alternatives in fixed format source code.

For example

      !-ASIS
            DATA XOX /  1 , 0 , 1
           &         ,  0 , 1 , 0
           &         ,  1 , 0 , 1 /

!-ASIS can be used safely with any declaration statement. However it is, in general, not safe to use !-ASIS with executable statements or others, such as FORMAT, which may be labelled. In such cases SPAG leaves the statement label unchanged, even if the code is re-labelled. In other cases the original code may no longer be appropriate. For example, SPAG may reverse the logic in a block IF statement, or convert it to an ELSEIF. These changes will not be apparent in the output code if !-ASIS is used on executable statements. Despite these problems, we have, because of user demand, decided to retain the option of using !-ASIS on executable statements. SPAG issues a warning for every unsafe usage; it is up to the user to hand-check code when such a warning is issued.

Item 60 of the configuration file allows you to specify that all declaration statements should be treated as though prefaced by !-ASIS This switch is suppressed if item 4 specifies that declarations are to be rewritten.

2.7.11 The ANCHOR Directive

Occasionally, it may be desirable to prevent the relocation of a block of statements. This can be done by inserting a !-ANCHOR directive immediately before the first statement of the block. The first statement of a block is preceded by a conditional or unconditional control transfer, such as an IF or GOTO statement. Subsequent statements in the block are always executed if first is executed. The anchored statement must be an executable statement. Note that even when a statement is anchored, it is possible that surrounding code may be relocated in such a way that it appears to move. C-ANCHOR and *-ANCHOR are accepted as alternatives in fixed format source code.

2.7.12 Comments in Restructured Code

Comments are presumed to be attached to the statement that follows them, and as the code is restructured, are moved around in the same way as that statement. End-of-line comments are an exception, and are attached to the statement with which they share a line. Another exception is comments preceding statements which are removed from the program (e.g. CONTINUE or GOTO statements which are not needed in the unscrambled code). These are attached to the statement following the redundant statement. The net result is that SPAG preserves comments and usually puts them in the right place.

2.7.13 Using Case to make Programs more Readable

Because of its antiquity, Fortran has always been an upper-case language. Even today, some Fortran programmers stick entirely to upper-case, even though mixed case is permitted by every major compiler (and is standard Fortran 95). This is a shame, because an important opportunity to make programs self-documenting is being missed. SPAG can be configured to make full use of case to distinguish different types of symbol. For example you can make special symbols (such as PARAMETERs and subprogram names) upper case; symbols which refer to global data (such as COMMON variables and dummy arguments) could be capitalized (e.g. Arg); and local variables could be lower case. A scheme like this helps programmers to know what they are looking at without constant reference to the declaration section.

2.7.14 Symbol Table Output

If item 2 of spag.fig is set to 2, SPAG generates symbol table files which provide valuable information about data usage within subprograms. These files are used by GXCHK, the global cross check program, to generate an overview of data usage within an entire program. The process is analogous to the more familiar procedure of compiling and linking. The symbol tables are analogous to object files, and GXCHK works like a linker to combine them into a single global report. Indeed, the AUTOMAKE tool, which automates compiling and linking can also be used to rebuild the global report file.

Symbol tables are plain ASCII text files, the format of which is described in detail in Appendix A. Users are encouraged to develop their own applications using these files as input. For example it would be a simple matter to write a program to check conformance to local variable naming conventions.

2.7.15 Some Pitfalls

2.8 Dynamic Analysis

2.8.1 What is it?

Dynamic analysis is a simple procedure for validating the operation of a program at run-time. It involves setting up a test version of your program containing extra checking code. This test program is compiled and linked in the normal way. The executable code appears to the user to operate in exactly the same way as the original, though it is larger and slower. If the checking code detects an error, it writes details to a log-file (probes.log), and continues execution. The programmer can then view the log-file at leisure after the run completes.

2.8.2 Worked Example

The example below shows how dynamic analysis works:

      SUBROUTINE QKNUM(Ival,Pos)
      INTEGER Ival , Pos , vals(50) , ios
      READ(11,*,IOSTAT=ios) vals
      IF ( ios.EQ.0 ) THEN
         DO Pos = 1 , 50
            IF ( Ival.EQ.vals(Pos) ) RETURN
         ENDDO
      ENDIF
 100  Pos = 0
      END

QKNUM is a simple routine which reads a list of 50 integers from a data file; the position of the first in the list with value Ival is returned in Pos. If none is equal to Ival, or if an error or end-of-line condition occurs while reading the list, Pos is returned as 0.

Most of the time, QKNUM will probably work well. Compilers and static analyzers are unlikely to find anything wrong with it, but nevertheless, it does contain a logical flaw. If the data file contains:

 2,9,,11,20*44,26*

Then items 1, 2 and 4 to 24 are assigned values. Items 3 and 25 to 50 remain unchanged from their initial (undefined) value. The result of the test on line 6

 IF ( Ival.EQ.vals(Pos) ) RETURN

is therefore undefined, and the program will behave unpredictably.

Whilst this particular bug is not common, it does illustrate, in a compact example, two features which are common to many bugs, and which defeat static analysis:

A version of QKNUM modified to perform a dynamic analysis might appear as follows:

      SUBROUTINE QKNUM(Ival,Pos)
      INTEGER Ival , Pos , vals(50) , ios
! insert 'undefined' value in ios
      CALL UNDFI(ios)
! insert 'undefined' value in every element of vals
      CALL UNDFIA(vals,50)
      READ(11,*,IOSTAT=ios) vals
! check that ios is defined
      IF ( ios.EQ.UNDEF ) CALL ERROR
      IF ( ios.EQ.0 ) THEN
         DO Pos = 1 , 50
! check that Ival and vals(Pos) are defined
            IF ( Ival.EQ.UNDEF ) CALL ERROR
            IF ( vals(pos).EQ.UNDEF ) CALL ERROR
            IF ( Ival.EQ.vals(Pos) ) RETURN
         ENDDO
      ENDIF
 100  Pos = 0
      END

The modified code contains a new section which initializes all local variables to a special 'undefined' value. This section is executed immediately after QKNUM is entered. Dummy arguments and COMMON variables are NOT initialized - they are assumed to be initialized higher up the call tree.

In addition a test is inserted wherever the value of a variable is used. For example, the test

 IF ( Ival.EQ.vals(Pos) ) RETURN

is meaningless if either Ival or vals(Pos) is undefined. The code therefore contains tests for both items. The variable Pos is also used, and, at first sight it may appear necessary to check Pos before testing vals(Pos). However, in this case we can deduce that Pos must be defined because it is a DO loop variable.

A version of QKNUM modified in this way will log an error when the input data leaves elements of vals undefined.

2.8.3 Dynamic Analysis using SPAG

When the dynamic analysis option of SPAG is activated (by setting item 1 of the configuration data to 4), it modifies the program to perform a dynamic analysis. The details differ from the above example, but the principles are identical. The modifications are automatic and require no user intervention. SPAG knows about variables initialized in DATA or type statements, and does not re-initialize them. It also distinguishes static variables (specified using SAVE) from others. Static variables are initialized only once - when first entering the subprogram that defines them. Other local variables are re-initialized to the undefined value every time a subprogram is entered. If item 36 of the configuration data is set to 1, all local variables are assumed to be static. This is probably the safest starting point for programs of unknown history.

The SPAG Dynamic analysis tool works with Fortran 77 and Fortran 95 input code. However the instrumentation may currently be incomplete when certain Fortran 95 features, such as derived types, are present. Although the instrumented code is fully functional, dynamic analysis may not report some illegal uses of undefined data involving these cases. SPAG does deal correctly with many Fortran 95 features, such as allocatable arrays and array sections.

The instrumentation code inserted by SPAG may make use of the standard Fortran 95 SIZE and KIND intrinsic functions. If the target code overrides the default meaning of those symbols, for example by using them as variable names, the resulting code will fail to compile. In such cases some manual intervention may be required to resolve the conflict.

2.8.4 COMMON Variables

One area which may benefit from a little attention from the user is the initialization of COMMON variables to the undefined value. If this is not done, the instrumented dynamic analysis code will be fully functional, but will not report errors involving COMMON variables that have not been initialized.

SPAG inserts code to initialize COMMON variables at the start of the main program immediately after the program starts. It follows that all COMMON variables must be available within the main program. This may require some hand-edits to your program, perhaps to insert some INCLUDE statements in the main program. SPAG does not do this automatically, but GXCHK (see Chapter 3) will tell you of any COMMON blocks which are not specified in the main program.

2.8.5 Initialized COMMON Variables

One final precaution is necessary: COMMON variables may be initialized using DATA statements in a BLOCKDATA subprogram. Many compilers also allow COMMON variables to be initialized in other ways, for example, in type statements within a subprogram. We must ensure that the initialization code generated by SPAG does not overwrite these genuine initializations in BLOCKDATAs or elsewhere.

This is achieved by running GXCHK (see Chapter 3) after SPAG has finished. If COMMON variables are initialized anywhere in the source code, GXCHK modifies the dynamic analysis version of the main program to remove the statements which would overwrite the initialization. This process is automatic, and requires no user intervention.

2.8.6 Summary

The steps required to perform a dynamic analysis of a Fortran program using SPAG and GXCHK are as follows:

2.8.7 False Positives

It is possible, though extremely rare in practice, for the probes to report an error when there is none. This happens if the 'undefined' bit pattern occurs as the normal result of a calculation within the program. For 32 and 64 bit data items, this possibility is vanishingly small. Even for 8 and 16 bit data, it happens sufficiently rarely that, for most users, the benefits of the test will outweigh the inconvenience of checking for false positives.

Item 35 of the SPAG configuration data specifies the minimum size (in bytes) of numeric and logical data items to be tested using dynamic analysis.

Item 35 does not apply to CHARACTER strings, because their length is not generally known until run-time. Instead, there is a test within the probe itself. Users may modify the source code of the probe as required (see Appendix B).

2.8.8 Mixed Code

Code containing dynamic analysis probes can be mixed freely with normal code. Similarly, the use of external object files and libraries presents no problems. Of course, checks will only be performed within instrumented code, and checks are inneffective when applied to data which has not been intitialized to the undefined value.

2.8.9 EQUIVALENCE & Dynamic Analysis

Dynamic analysis is not put off by EQUIVALENCEd data, or by COMMON blocks whose structure varies from one subprogram to another (provided that the version which appears in the main program includes the entire data area).

2.8.10 Static or Dynamic?

SPAG and GXCHK offer facilities for both static and dynamic analysis, and the two are complimentary. Each excels where the other is deficient.

A static analyzer checks all your code, whether it executes or not; it produces useful results without the need to perform test runs; it produces documentation which is of general use to managers and staff working on the program. On the other hand, a static analyzer cannot hope to predict the effect of external data on your program's execution, or to monitor the status of every byte of memory.

Dynamic analysis, on the other hand, operates when your program is running with real data, and can monitor memory at the byte level. The main disadvantage of dynamic analysis is that, unlike static analysis, it does not check all your code, only the parts that execute in a given run.

This disadvantage can be overcome to some degree by using coverage analysis (see Section 2.9 and Chapter 4) to ensure that your test program exercises all of your source code.

Working together, static, dynamic, and coverage analysis provide an unrivalled health check for your programs.

2.9 Coverage Analysis

SPAG contains facilities for inserting probes into Fortran source code for the purpose of analyzing the run-time performance. These probes may be used to detect execution hot-spots, identify untested code-blocks, or provide a profile of CPU usage.

If item 6 of the configuration data is set to 2, SPAG prepares your code for coverage analysis testing by:

A weaker form of program tracing is invoked by setting item 6 of the SPAG configuration data to 1. With this setting, tracing takes place only at the subprogram level, and no coverage analysis data files are produced. In conjunction with the plusFORT probe routines this is sufficient to produce a profile of CPU usage by subprogram like that shown below. By default, the report is written to a file called probes.log. However, this and other details can be adjusted by modifying the source of the probe routines which may be found in the installation directory.

  ------------------------------------------------
 |                 Timing Reports                 |
 |     Job completed after      35.71 seconds     |
 |    Routines taking >1% of total shown below    |
  ------------------------------------------------

              MATTEST          4 calls   0% --------------------------------------------------
                   NF          1 calls   0% --------------------------------------------------
                 NFCG          4 calls   4% **------------------------------------------------
           NF3DPRECON        137 calls   3% **----------------------------------------
           NF2DPRECON      27381 calls   9% *****-----------------------------------
             TRISOLVE    5763813 calls  72% ************************************
              SPMMULT        141 calls  11% ******
              GETGI3D          4 calls   0% -
              GETGI2D        396 calls   0% -

(* excludes called routines, - includes them)
    5791881 Intervals timed

See Appendix B for further details.

Either form of coverage analysis depends on data written when the test program finishes. If the program is interrupted, or if execution is halted by a routine which does not contain probes, the required data is not written, and coverage analysis fails for this run (though no damage is done to the data files). This can happen if you call a system routine (e.g. CALL EXIT) to halt execution. The problem may be avoided by inserting a call to the program termination probe, SPAG_PR_EXI, just before the call to EXIT.

      CALL SPAG_PR_EXI
      CALL EXIT(16) 

If dynamic analysis (section 2.8) is activated, item 6 is automatically set to 1.

2.10 Processing Fortran 95 Modules

The USE statement in Fortran 95 allows a subprogram to import data and interfaces from a separately compiled module. It is in many ways analagous to an INCLUDE statement, and is generally recommended as a superior replacement for both INCLUDE and COMMON. In order to interpret USE correctly, SPAG must import information about the external module. It does this by reading the symbol table generated when SPAG processed the module. This gives rise to 2 problems:-