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:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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:
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.
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.
Item 1 |
Source Code OutputSpecifies what source code output is required - possible values are:
|
||||||||||||||||||||||||||
Item 2 |
Symbol TablesSpecifies whether the symbol tables generated by SPAG are to be written to files - possible values are:
|
||||||||||||||||||||||||||
Item 3 |
Clutter ChecksSpecifies how SPAG should deal with clutter (unused variables, PARAMETERs, COMMON blocks etc.). Possible values are:
|
||||||||||||||||||||||||||
Item 4 |
Declaration StandardizationSpecifies whether and how SPAG should reorganize the declarations in a subprogram. Possible values are:
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 RenamingSpecifies whether SPAG should change the name of symbols before writing the restructured output file. Possible values are:
|
||||||||||||||||||||||||||
Item 6 |
Timing, Test Coverage and Hot-spot AnalysisSpecifies 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:
See section 2.9 for further discussion of coverage analysis. |
||||||||||||||||||||||||||
Item 7 |
Complexity MetricsSpecifies whether SPAG should compute complexity metrics for each subprogram. Possible values are:
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:
|
||||||||||||||||||||||||||
Item 8 |
Interface Summary ReportsSpecifies 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:
|
||||||||||||||||||||||||||
Item 9 |
Source File re-orderingBy 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:
|
||||||||||||||||||||||||||
Item 10 |
Output FilesSpecifies how output files (both restructured source and symbol tables) are to be created. Possible values are:
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 OutputSpecifies how newly created subprogram interface modules are to be written (item 15 controls whether SPAG creates new interface modules). Possible values are:
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 FunctionsSpecifies 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:
|
||||||||||||||||||||||||||
Item 13 |
Conversion of isolated COMMON to Fortran 95 modulesIf 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 argumentsIf 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 HeaderSpecifies 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:
|
||||||||||||||||||||||||||
Item 20 |
Computed GOTOSpecifies whether computed GOTOs are to be converted to IF..THEN..ELSE or SELECT CASE constructs. Possible values are:
The translations deal correctly with the case where i is less than 1 or greater than 3. |
||||||||||||||||||||||||||
Item 21 |
SELECT CASESpecifies 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:
|
||||||||||||||||||||||||||
Item 22 |
Arithmetic IFSpecifies whether arithmetic IFs are to be converted to IF..THEN..ELSE constructs. Possible values are:
|
||||||||||||||||||||||||||
Item 23 |
Logical IF + GOTOSpecifies whether logical IF followed by GOTO is to be converted to a block IF..THEN..ELSE construct. Possible values are:
|
||||||||||||||||||||||||||
Item 24 |
DO WHILESpecifies 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:
|
||||||||||||||||||||||||||
Item 25 |
EXIT and CYCLESpecifies 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:
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 ReplicationSpecifies whether SPAG may replicate individual statements if this improves the readability of a section of code. Possible values are:
In this example, structure is improved by duplicating the statement n = n + 1. |
||||||||||||||||||||||||||
Item 27 |
Relocation into DO loopSpecifies 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:
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 ENDSpecifies 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:
|
||||||||||||||||||||||||||
Item 29 |
Remove Unreachable CodeSpecifies whether blocks of code which could never be executed are to be removed. Possible values are:
Unreachable Code ExampleGOTO 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 StructuresSpecifies whether SPAG should translate VAX STRUCTURE and RECORD statements, and the corresponding structure references, to Fortran 95 derived types. Possible values are:
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:
|
||||||||||||||||||||||||||
Item 31 |
Dispatch Loops and Internal SubroutinesSpecifies 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.
Refer to section 2.7.1 for more discussion of this and other restructuring options. |
||||||||||||||||||||||||||
Item 35 |
Dynamic Analysis Element SizeSpecifies 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 VariablesSpecifies 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:
See section 2.8 for further discussion of dynamic analysis. |
||||||||||||||||||||||||||
Item 40 |
Relocate FORMAT statementsSpecifies whether FORMAT statements are to be grouped together just before the END statement of each subprogram. Possible values are:
|
||||||||||||||||||||||||||
Item 41 |
Use of CONTINUESpecifies whether CONTINUE statements should be inserted whenever a label is required (except on FORMAT statements). Possible values are:
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.
This control is effective only for re-structuring runs (Item 1). |
||||||||||||||||||||||||||
Item 42 |
Single statement block IFsSpecifies whether block IFs are to be converted to logical IFs if the block contains only one statement. Possible values are:
|
||||||||||||||||||||||||||
Item 43 |
Logic Reversal in block IFSpecifies 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:
|
||||||||||||||||||||||||||
Item 44 |
Insert Redundant ELSESpecifies whether SPAG should add a null ELSE block to block IFs that have an ELSEIF but no ELSE. Possible values are:
|
||||||||||||||||||||||||||
Item 45 |
DO loopsSpecifies which style of DO loop is to be used. Possible values are:
|
||||||||||||||||||||||||||
Item 46 |
Character ConstantsSpecifies 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:
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 OperatorsSpecifies whether the new Fortran 95 style relational operators are to be used. Possible values are:
Both styles of operator are permitted by the Fortran 95 standard. |
||||||||||||||||||||||||||
Item 48 |
Fortran 95 END SUBROUTINE statementsSpecifies whether SPAG should translate END statements to Fortran 95 END SUBROUTINE, END PROGRAM, END FUNCTION etc. Possible values are:
|
||||||||||||||||||||||||||
Item 49 |
Remove Redundant RETURN and STOPSpecifies 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:
|
||||||||||||||||||||||||||
Item 51 |
Input Statement LengthSpecifies 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 AccessSpecifies whether unpaired apostrophes as used in old-style direct access (using DEFINE FILE, FIND etc.) are to be allowed. Possible values are:
|
||||||||||||||||||||||||||
Item 54 |
PDP style PARAMETERSpecifies 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:
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 ArgumentsSpecifies 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:
|
||||||||||||||||||||||||||
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:
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 StatementsSpecifies whether embedded EXEC SQL statements, as used with IBM's DB2 database are permitted. Possible values are:
|
||||||||||||||||||||||||||
Item 60 |
Reformat Declaration StatementsSpecifies 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:
|
||||||||||||||||||||||||||
Item 61 |
Tab CharactersSpecifies 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:
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 TOSpecifies whether GOTO should be spelled with a space in the restructured output code. Possible values are:
|
||||||||||||||||||||||||||
Item 63 |
ENDIF or END IFSpecifies 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:
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 DATASpecifies whether BLOCKDATA should be spelled with a space in the restructured output code. Possible values are:
|
||||||||||||||||||||||||||
Item 65 |
DOUBLEPRECISION or DOUBLE PRECISIONSpecifies whether DOUBLEPRECISION should be spelled with a space in the restructured output code. Possible values are:
|
||||||||||||||||||||||||||
Item 66 |
Space after Fortran KeywordsSpecifies whether a space is required after Fortran keywords. Possible values are:
|
||||||||||||||||||||||||||
Item 67 |
Spaces around + and -Specifies whether a space is to be placed on either side of + or - operators. Possible values are:
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:
For example: ! Item 68 = 0 Z=X*Y ! Item 68 = 1 Z = X*Y |
||||||||||||||||||||||||||
Item 69 |
Spaces around Logical OperatorsSpecifies whether a space is required on either side of logical operators (.AND., .OR., .EQV., .NEQV., .XOR. etc.). Possible values are:
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 CommasSpecifies whether a space is required on either side of commas. Possible values are:
|
||||||||||||||||||||||||||
Item 71 |
Spaces in IF statementSpecifies whether a space should be inserted on either side of the test expression in IF statements. Possible values are:
For Example ! Item 71 = 0 IF (Q1) THEN ! Item 71 = 1 IF ( Q1 ) THEN |
||||||||||||||||||||||||||
Item 73 |
Spaces around Concatenation OperatorSpecifies whether a space is required on either side of the concatenation operator ('//'). Possible values are:
For example ! item 73 = 0 zname = '*.'//zextn ! item 73 = 1 zname = '*.' // zextn |
||||||||||||||||||||||||||
Item 79 |
Separate Declaration for every variableSpecifies 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:
|
||||||||||||||||||||||||||
Item 80 |
Preserve Statement LabelsSpecifies whether statement labels are to be preserved. Possible values are:
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 BaseInitial 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 0Label increment for un-indented code - i.e. labelled statements which are not inside any DO or block IF constructs. |
||||||||||||||||||||||||||
Item 83 |
Label Increment 1Label increment for code at indentation level 1 - i.e. labelled statements inside only one DO or block IF construct. |
||||||||||||||||||||||||||
Item 84 |
Label Increment 2Label increment for code at indentation level 2. |
||||||||||||||||||||||||||
Item 85 |
Label Increment 3Label increment for code at indentation level 3. |
||||||||||||||||||||||||||
Item 86 |
Label Increment 4Label increment for code at indentation level 4. |
||||||||||||||||||||||||||
Item 87 |
Label Increment 5Label increment for code at indentation level 5 or above. |
||||||||||||||||||||||||||
Item 88 |
Format Label BaseInitial 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 IncrementFORMAT 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 JustificationSpecifies how statement labels are to be formatted. Possible values are:
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 VariablesSpecifies what case conversion is done for local variable names. Possible values are:
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 variablesSpecifies what case conversion is done for COMMON variable names. Possible values are as detailed for item 100. |
||||||||||||||||||||||||||
Item 102 |
Case of Dummy ArgumentsSpecifies what case conversion is done for dummy argument names. Possible values are as detailed for item 100. |
||||||||||||||||||||||||||
Item 103 |
Case of PARAMETERsSpecifies what case conversion is done for PARAMETER names. Possible values are as detailed for item 100. |
||||||||||||||||||||||||||
Item 104 |
Case of other SymbolsSpecifies 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 operatorsSpecifies what case conversion is done for logical and relational operators (.AND., .NE. etc.). Possible values are:
|
||||||||||||||||||||||||||
Item 107 |
Case of Fortran keywordsSpecifies what case conversion is done for Fortran keywords (READ, THEN, INTEGER etc.). Possible values are:
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 StringsSpecifies what case conversion is done for character strings. Possible values are:
|
||||||||||||||||||||||||||
Item 109 |
Case of CommentsSpecifies what case conversion is done for comments (from column 2 onwards). Possible values are:
|
||||||||||||||||||||||||||
Item 121 |
Right Margin for OutputThe 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 OutputThe 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 IndentationThe 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 IndentationThe 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 CharacterThe 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 CharacterThe 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 JustificationSpecifies whether SPAG should reformat the text of comments. Possible values are:
|
||||||||||||||||||||||||||
Item 130 |
COMMON Block Alignment TestsSpecifies whether SPAG should check the alignment of variables and mixed CHARACTER/non-CHARACTER variables in COMMON blocks. Possible values are:
|
||||||||||||||||||||||||||
Item 131 |
Floating Point Equality TestSpecifies whether SPAG should report the existence of unsafe tests for equality of floating point data. Possible values are:
|
||||||||||||||||||||||||||
Item 132 |
Variable Name ChecksSpecifies 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:
|
||||||||||||||||||||||||||
Item 133 |
Subprogram Argument ChecksSpecifies 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:
|
||||||||||||||||||||||||||
Item 134 |
Unused Variable ChecksSpecifies 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:
|
||||||||||||||||||||||||||
Item 135 |
Used but not Set ChecksSpecifies 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:
|
||||||||||||||||||||||||||
Item 136 |
Unused COMMON blocks and INCLUDE filesSpecifies 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:
|
||||||||||||||||||||||||||
Item 138 |
Conversion of #include to INCLUDESpecifies whether SPAG should convert C preoprocessor includes (using #include) to standard Fortran 95 format (using an INCLUDE statement). Possible values are:
|
||||||||||||||||||||||||||
Item 140 |
Input Source FormSpecifies the format of the input source code. Possible values are:
|
||||||||||||||||||||||||||
Item 141 |
Output Source FormSpecifies the required format of the output source code. Possible values are:
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 NamesThe 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 RenamesThe 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 IdentifierFour 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 MarkerWhen 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 MarkerSpecifies 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 StatementIf 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 ExtensionThe 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 ExtensionThe 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 ExtensionThe 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 ExtensionThe 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 SPAGA 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 1A 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 2A 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 RootA 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 RoutineIf 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 RoutineIf 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 RoutineIf 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 RoutineIf 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 RoutineIf 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. |
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:
Item 27 is set to 0 by default. This prohibits code relocation into an indexed DO loop, because SPAG cannot currently be sure whether the relocated code changes the DO loop variable. That would be a technical violation of the ISO standard, and compilers may, quite properly, report an error when it happens. However in most cases the relocated code does not alter the DO loop variable, and even if it did, the violation is a technical one, because the loop is exited immediately after the relocated code is executed.
If item 27 is instead set to 1, you may be able to improve the restructured code somewhat, but run the risk that the SPAGged code may trigger a compiler error.
Item 26 is set to 1 by default. This allows individual statements to be replicated if it appears that it may help inprove program structure. If it is set to 0, SPAG will typically find other ways (extra internal subroutines, or dispatch loop cases) to change the structure, which may or may not be preferred.
The use of an !-ANCHOR directive (see section 2.7.10) in the input code forces SPAG to leave the following statement in place, and may result in a different output structure.
Item 31 allows users to select between two alternatives - dispatch loops and internal subroutines - for removing GOTOs that remain after first stage restructuring. The following example illustrates the difference:
Before Restructuring |
---|
FUNCTION IUNITS(JDIF,KDIF,JU,KU,IP) INTEGER IP,IUNITS,JDIF,JU,KDIF,KU IUNITS=1 IF(JDIF)20,30,40 20 JU=-1 IF(KDIF)50,60,70 30 JU=0 KU=KDIF/IABS(KDIF) IP=4 RETURN 40 JU=1 IF(KDIF)70,60,50 50 IF(JDIF-KDIF)100,51,100 51 KU=JU IP=3 RETURN 60 KU=0 IP=4 RETURN 70 IF(JDIF+KDIF)100,71,100 71 KU=-JU IP=3 RETURN 100 IUNITS=0 RETURN END |
spag iunits.for 31=1 (Dispatch Loops) |
spag iunits.for 31=2 (Internal Subroutines) |
---|---|
FUNCTION iunits(Jdif,Kdif,Ju,Ku,Ip) IMPLICIT NONE INTEGER Ip , iunits , Jdif , Ju , Kdif , Ku INTEGER :: spag_nextblock_1 spag_nextblock_1 = 1 SPAG_DispatchLoop_1: DO SELECT CASE (spag_nextblock_1) CASE (1) iunits = 1 IF ( Jdif<0 ) THEN Ju = -1 IF ( Kdif<0 ) THEN ELSEIF ( Kdif==0 ) THEN spag_nextblock_1 = 2 CYCLE SPAG_DispatchLoop_1 ELSE spag_nextblock_1 = 3 CYCLE SPAG_DispatchLoop_1 ENDIF ELSEIF ( Jdif==0 ) THEN Ju = 0 Ku = Kdif/iabs(Kdif) Ip = 4 RETURN ELSE Ju = 1 IF ( Kdif<0 ) THEN spag_nextblock_1 = 3 CYCLE SPAG_DispatchLoop_1 ENDIF IF ( Kdif==0 ) THEN spag_nextblock_1 = 2 CYCLE SPAG_DispatchLoop_1 ENDIF ENDIF IF ( Jdif/=Kdif ) THEN iunits = 0 RETURN ELSE Ku = Ju Ip = 3 RETURN ENDIF CASE (2) Ku = 0 Ip = 4 RETURN CASE (3) IF ( Jdif+Kdif/=0 ) THEN iunits = 0 ELSE Ku = -Ju Ip = 3 RETURN ENDIF EXIT SPAG_DispatchLoop_1 END SELECT ENDDO SPAG_DispatchLoop_1 END FUNCTION iunits |
FUNCTION iunits(Jdif,Kdif,Ju,Ku,Ip) IMPLICIT NONE INTEGER Ip , iunits , Jdif , Ju , Kdif , Ku iunits = 1 IF ( Jdif<0 ) THEN Ju = -1 IF ( Kdif<0 ) THEN ELSEIF ( Kdif==0 ) THEN CALL spag_block_1 RETURN ELSE CALL spag_block_2 RETURN ENDIF ELSEIF ( Jdif==0 ) THEN Ju = 0 Ku = Kdif/iabs(Kdif) Ip = 4 RETURN ELSE Ju = 1 IF ( Kdif<0 ) THEN CALL spag_block_2 RETURN ENDIF IF ( Kdif==0 ) THEN CALL spag_block_1 RETURN ENDIF ENDIF IF ( Jdif/=Kdif ) THEN iunits = 0 RETURN ELSE Ku = Ju Ip = 3 RETURN ENDIF CONTAINS SUBROUTINE spag_block_1 Ku = 0 Ip = 4 RETURN END SUBROUTINE spag_block_1 SUBROUTINE spag_block_2 IF ( Jdif+Kdif/=0 ) THEN iunits = 0 ELSE Ku = -Ju Ip = 3 RETURN ENDIF END SUBROUTINE spag_block_2 END FUNCTION iunits |
In general, the internal subroutine approach produces clearer code for simple cases like this. In more complex cases, the opposite is true. Internal subroutines may then be called recursively, and it's harder to understand what is going on. There is no limit in principle to how deep the recursion may go. The default for Item 31 is 2, which seeks to get the best of both worlds.
SPAG does not currently restructure some structures which cause a GOTO-like jump. These are:
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 |
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:
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.
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.
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.
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.
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).
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.
* AMOD10 mod AMIN1 min 'test1.prv' fchsta firstc zsyst zzzsys /pkchAR/ /pkcomm/ swstx2 ityp jtyp jtyp ityp lopbr lclbr lclbr lopbr swfrag oxo xox
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.
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.
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.
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.
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.
IF ( A1*FUNC(X) ) 100,200,300
becomes
TEMP = A1*FUNC(X) IF ( TEMP ) 100,200,300
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.
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.
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.
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.
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.
The steps required to perform a dynamic analysis of a Fortran program using SPAG and GXCHK are as follows:
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).
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.
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).
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.
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.
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:-
To do this, SPAG maintains a ASCII text file called PFMODULE.KEY. This file contains records which specify the location of each module USEd by source code in the current directory. For example
TRAJECTORY : traject.smb
specifies that the symbol table for module TRAJECTORY is in the file traject.smb.
As SPAG processes source code, it automatically inserts records in PFMODULE.KEY for every module it encounters. When SPAG encounters a USE statement, it consults this file to find the module data.
An exception to this rule is that SPAG will not automatically find modules from a different directory. In this case it is recommended that the locations of modules be inserted in PFMODULE.KEY using a text editor.
GPS_COORDS : gps\coordmod.smb
By default SPAG automatically reorders input files to ensure that modules are processed before code that USEs them (see Item 9). This works well if both MODULE and USEr are processed by SPAG in a single run. It is recommended that, wherever possible, multiple input files, possibly using wildcards, should be processed in a single, rather than multiple invocations of spag.
If SPAG encouters a USE statement for a module that has not been not processed in the current run, it looks in PFMODULE.KEY to find the location of the symbol data for that module. If it finds that there is no entry, because the module has not been processed yet, it reports an error. It's also possible that, if a module is changed, SPAG may use an obselete symbol table, and produce incorrect results while appearing to work correctly. This is similar to the problem, familiar to most Fortran programmers, that occurs when modules are not compiled before code that USEs them.
The plusFORT AUTOMAKE tool provides an alternative solution to this problem (just as it does for compilers). It is recommended for cases where SPAG must be invoked separately for each input file. The plusFORT installation directory contains a batch/shell file called Autospag, which uses AUTOMAKE to run SPAG in such a way that the processing order requirements are satisfied. A small configuration file called autospag.fig should be placed in the source directory. A typical case, in which all .f90 files are to be processed, and the symbol table files have the .smb extension would be handled by the following configuration file:
COMPILE=@spag %fi OBJEXT=SMB FILES=*.f90 NOQUITONERROR
Then to run SPAG on all .f90 files in the current directory, simply type Autospag. More complicated cases can be handled with suitable changes to autospag.fig (see the AUTOMAKE documentation)