Go to the first, previous, next, last section, table of contents.


Collected Fortran Wisdom

Most users of g77 can be divided into two camps:

Users writing new code generally understand most of the necessary aspects of Fortran to write "mainstream" code, but often need help deciding how to handle problems, such as the construction of libraries containing `BLOCK DATA'.

Users dealing with "legacy" code sometimes don't have much experience with Fortran, but believe that the code they're compiling already works when compiled by other compilers (and might not understand why, as is sometimes the case, it doesn't work when compiled by g77).

The following information is designed to help users do a better job coping with existing, "legacy" Fortran code, and with writing new code as well.

Overly Convenient Command-line Options

These options should be used only as a quick-and-dirty way to determine how well your program will run under different compilation models without having to change the source. Some are more problematic than others, depending on how portable and maintainable you want the program to be (and, of course, whether you are allowed to change it at all is crucial).

You should not continue to use these command-line options to compile a given program, but rather should make changes to the source code:

-finit-local-zero
(This option specifies that any uninitialized local variables and arrays have default initialization to binary zeros.) Many other compilers do this automatically, which means lots of Fortran code developed with those compilers depends on it. It is safer (and probably would produce a faster program) to find the variables and arrays that need such initialization and provide it explicitly via `DATA', so that `-finit-local-zero' is not needed. Consider using `-Wuninitialized' (which requires `-O') to find likely candidates, but do not specify `-finit-local-zero' or `-fno-automatic', or this technique won't work.
-fno-automatic
(This option specifies that all local variables and arrays are to be treated as if they were named in `SAVE' statements.) Many other compilers do this automatically, which means lots of Fortran code developed with those compilers depends on it. The effect of this is that all variables and arrays are made static, that is, not placed on the stack or in heap storage. This might cause a buggy program to appear to work better. If so, rather than relying on this command-line option (and hoping all compilers provide the equivalent one), add `SAVE' statements to some or all program unit sources, as appropriate. Consider using `-Wuninitialized' (which requires `-O') to find likely candidates, but do not specify `-finit-local-zero' or `-fno-automatic', or this technique won't work. The default is `-fautomatic', which tells g77 to try and put variables and arrays on the stack (or in fast registers) where possible and reasonable. This tends to make programs faster.
-fugly
Fix the source code so that `-fno-ugly' will work. Note that, for many programs, it is difficult to practically avoid using the features enabled via `-fugly-init', and these features pose the lowest risk of writing nonportable code, among the various "ugly" features.
-fgroup-intrinsics-hide
Change the source code to use `EXTERNAL' for any external procedure that might be the name of an intrinsic. It is easy to find these using `-fgroup-intrinsics-disable'.

Block Data and Libraries

To ensure that block data program units are linked, especially a concern when they are put into libraries, give each one a name (as in `BLOCK DATA FOO') and make sure there is an `EXTERNAL FOO' statement in every program unit that uses any common block initialized by the corresponding `BLOCK DATA'. g77 currently compiles a `BLOCK DATA' as if it were a `SUBROUTINE', that is, it generates an actual procedure having the appropriate name. The procedure does nothing but return immediately if it happens to be called. For `EXTERNAL FOO', where `FOO' is not otherwise referenced in the same program unit, g77 assumes there exists a `BLOCK DATA FOO' in the program and ensures that by generating a reference to it so the linker will make sure it is present. (Specifically, g77 outputs in the data section a static pointer to the external name `FOO'.)

The implementation g77 currently uses to make this work is one of the few things not compatible with f2c as currently shipped. f2c currently does nothing with `EXTERNAL FOO' except issue a warning that `FOO' is not otherwise referenced, and for `BLOCK DATA FOO', f2c doesn't generate a dummy procedure with the name `FOO'. The upshot is that you shouldn't mix `f2c' and g77 in this particular case. If you use f2c to compile `BLOCK DATA FOO', then any g77-compiled program unit that says `EXTERNAL FOO' will result in an unresolved reference when linked. If you do the opposite, then `FOO' might not be linked in under various circumstances (such as when `FOO' is in a library, or you're using a "clever" linker--so clever, it produces a broken program with little or no warning by omitting initializations of global data because they are contained in unreferenced procedures).

The changes you make to your code to make g77 handle this situation, however, appear to be a widely portable way to handle it. That is, many systems permit it (as they should, since the FORTRAN 77 standard permits `EXTERNAL FOO' when `FOO' is a block data program unit), and of the ones that might not link `BLOCK DATA FOO' under some circumstances, most of them appear to do so once `EXTERNAL FOO' is present in the appropriate program units.

Faster Programs

Aside from the usual `gcc' options, such as `-O', `-ffast-math', and so on, consider trying:

-fno-f2c
Use this if you aren't linking with any code compiled using f2c. (Note that libf2c is not an example of code that is compiled using f2c---it is compiled by a C compiler, usually gcc.)

If you're using `-fno-automatic' already, you probably should change your code to allow compilation with `-fautomatic' (the default), to allow the program to run faster.

Similarly, you should be able to use `-fno-init-local-zero' (the default) instead of `-finit-local-zero'. This is because it is rare that every variable affected by these options in a given program actually needs to be so affected.

For example, `-fno-automatic', which effectively `SAVE's every local variable and array, affects even things like `DO' iteration variables, which rarely need to be `SAVE'd, and this often reduces run-time performances. Similarly, `-fno-init-local-zero' forces such variables to be initialized to zero--when `SAVE'd (such as when `-fno-automatic'), this by itself generally affects only startup time for a program, but when not `SAVE'd, it can slow down the procedure every time it is called.

See section Overly Convenient Command-line Options, for information on the `-fno-automatic' and `-finit-local-zero' options and how to convert their use into selective changes in your own code.

Working Programs

Getting Fortran programs to work in the first place can be quite a challenge--even when the programs already work on other systems, or when using other compilers.

g77 offers some options that might be useful for tracking down bugs in such programs. See section Option Summary, for a summary of these and other options, and cross-references for each such option to the pertinent material in this manual.

-finit-local-zero
A program that works better when compiled with this option is depending on a particular system's, or compiler's, tendency to initialize some variables to zero. It might be worthwhile finding such cases and fixing them.
-fno-automatic
A program that works better when compiled with this option is depending on not having to use the `SAVE' statement as required by the Fortran standard. It might be worthwhile finding such cases and fixing them.
-Wimplicit
This might catch failures to properly specify the types of variables, arrays, and functions in the code. However, in code that makes heavy use of Fortran's implicit-typing facility, this option might produce so many warnings about cases that are working, it would be hard to find the one or two that represent bugs.
-Wunused
This can find bugs involving implicitly typing, sometimes more easily than using -Wimplicit in code that makes heavy use of implicit typing. An unused variable or array might indicate that the spelling for its declaration is different from that of its intended uses.
-Wuninitialized
This can find bugs involving uninitialized variables, which can in turn result from misspellings in declaration statements.
-Wsurprising
This can find bugs involving expression evaluation or in the way `DO' loops with non-integral iteration variables are handled. Cases found by this option might indicate a difference of interpretation between the author of the code involved, and a standard-conforming compiler such as g77. Such a difference might produce actual bugs. In any case, changing the code to explicitly do what the programmer might have expected it to do, so g77 and other compilers are more likely to follow the programmer's expectations, might be worthwhile, especially if such changes make the program work better.
-W
It is possible that the "extra" warnings enabled by this option could expose bugs in the code.

Loops

The meaning of a `DO' loop in Fortran is precisely specified in the Fortran standard...and is quite different from what many programmers might expect.

In particular, Fortran `DO' loops are implemented as if the number of trips through the loop is calculated before the loop is entered.

The number of trips for a loop is calculated from the start, end, and increment values specified in a statement such as:

DO iter = start, end, increment

The trip count is evaluated using a fairly simple formula based on the three values following the `=' in the statement, and it is that trip count that is effectively decremented during each iteration of the loop. If, at the beginning of an iteration of the loop, the trip count is zero or negative, the loop terminates. The per-loop-iteration modifications to iter are not related to determining whether to terminate the loop.

There are two important things to remember about the trip count:

These two items mean that there are loops that cannot be written in straightforward fashion using the Fortran `DO'.

For example, on a system with the canonical 32-bit two's-complement implementation of `INTEGER', the following loop will not work:

DO I = -2000000000, 2000000000

Although the start and end values are well within the range of `INTEGER', the trip count is not. The expected trip count is 40000000001, which is outside the range of `INTEGER' on many systems.

Instead, the above loop should be constructed this way:

I = -2000000000
DO
  IF (I .GT. 2000000000) EXIT
  ...
  I = I + 1
END DO

The simple `DO' construct and the `EXIT' statement (used to leave the innermost loop) are F90 features that g77 supports.

Some Fortran compilers have buggy implementations of `DO', in that they don't follow the standard. They implement `DO' as a straightforward translation to what, in C, would be a `for' statement. Instead of creating a temporary variable to hold the trip count as calculated at run time, these compilers use the iteration variable iter to control whether the loop continues at each iteration.

The bug in such an implementation shows up when the trip count is within the range of the type of iter, but the magnitude of `ABS(end) + ABS(incr)' exceeds that range. For example:

DO I = 2147483600, 2147483647

A loop started by the above statement will work as implemented by g77, but the use, by some compilers, of a more C-like implementation akin to

for (i = 2147483600; i <= 2147483647; ++i)

produces a loop that does not terminate, because `i' can never be greater than 2147483647, since incrementing it beyond that value overflows `i', setting it to -2147483648. This is a large, negative number that still is less than 2147483647.

Another example of unexpected behavior of `DO' involves using a nonintegral iteration variable iter, such as a `REAL' or `DOUBLE PRECISION' variable. Consider the following program:

      DATA BEGIN, END, STEP /.1, .31, .007/
      DO 10 R = BEGIN, END, STEP
         IF (R .GT. END) PRINT *, R, ' .GT. ', END, '!!'
         PRINT *,R
10    CONTINUE
      PRINT *,'LAST = ',R
      IF (R .LE. END) PRINT *, R, ' .LE. ', END, '!!'
      END

A C-like view of `DO' would hold that the two "exclamatory" `PRINT' are never executed. However, this is the output of running the above program as compiled by g77 on a Linux ix86 system:

 .100000001
 .107000001
 .114
 .120999999
 ...
 .289000005
 .296000004
 .303000003
LAST =   .310000002
 .310000002 .LE.   .310000002!!

Note that one of the two checks in the program turned up an apparent violation of the programmer's expectation--yet, the loop is correctly implemented by g77, in that it has 30 iterations. This trip count of 30 is correct when evaluated using the floating-point representations for the begin, end, and incr values (.1, .31, .007) on Linux ix86 are used. On other systems, an apparently more accurate trip count of 31 might result, but, nevertheless, g77 is faithfully following the Fortran standard, and the result is not what the author of the sample program above apparently expected. (Such other systems might, for different values in the `DATA' statement, violate the other programmer's expectation, for example.)

Due to this combination of imprecise representation of floating-point values and the often-misunderstood interpretation of `DO' by standard-conforming compilers such as g77, use of `DO' loops with `REAL' or `DOUBLE PRECISION' iteration variables is not recommended. Such use can be caught by specifying `-Wsurprising'. See section Options to Request or Suppress Warnings, for more information on this option.

Advantages Over f2c

Without f2c, g77 would have taken much longer to do and probably not been as good for quite a while. Sometimes people who notice how much g77 depends on, and documents encouragement to use, f2c ask why g77 was created if f2c already existed.

This section gives some basic answers to these questions, though it is not intended to be comprehensive.

Language Extensions

g77 offers several extensions to the Fortran language that f2c doesn't.

However, f2c offers a few that g77 doesn't, like fairly complete support for `INTEGER*2'. It is expected that g77 will offer some or all of these missing features at some time in the future. (Version 0.5.18 of g77 offers some rudimentary support for some of these features.)

Compiler Options

g77 offers a whole bunch of compiler options that f2c doesn't.

However, f2c offers a few that g77 doesn't, like an option to generate code to check array subscripts at run time. It is expected that g77 will offer some or all of these missing options at some time in the future.

Compiler Speed

Saving the steps of writing and then rereading C code is a big reason why g77 should be able to compile code much faster than using f2c in conjunction with the equivalent invocation of gcc.

However, due to g77's youth, lots of self-checking is still being performed. As a result, this improvement is as yet unrealized (though the potential seems to be there for quite a big speedup in the future). It is possible that, as of version 0.5.18, g77 is noticably faster compiling many Fortran source files than using f2c in conjunction with gcc.

Program Speed

g77 has the potential to better optimize code than f2c, even when gcc is used to compile the output of f2c, because f2c must necessarily translate Fortran into a somewhat lower-level language (C) that cannot preserve all the information that is potentially useful for optimization, while g77 can gather, preserve, and transmit that information directly to the GBE.

For example, g77 implements `ASSIGN' and assigned `GOTO' using direct assignment of pointers to labels and direct jumps to labels, whereas f2c maps the assigned labels to integer values and then uses a C `switch' statement to encode the assigned `GOTO' statements.

However, as is typical, theory and reality don't quite match, at least not in all cases, so it is still the case that f2c plus gcc can generate code that is faster than g77.

It is hoped that version 0.5.18 of g77 will offer default settings and options that allow for better program speed, though it is not yet known whether these same options, when applied to a gcc compilation of f2c output, will improve the speed of programs compiled using that method as well.

Ease of Debugging

Because g77 compiles directly to assembler code like gcc, instead of translating to an intermediate language (C) as does f2c, support for debugging can be better for g77 than f2c.

However, although g77 might be somewhat more "native" in terms of debugging support than f2c plus gcc, there still are a lot of things "not quite right". Many of the important ones should be resolved in the near future.

For example, g77 doesn't have to worry about reserved names like f2c does. Given `FOR = WHILE', f2c must necessarily translate this to something other than `for = while;', because C reserves those words.

However, g77 does still uses things like an extra level of indirection for `ENTRY'-laden procedures--in this case, because the back end doesn't yet support multiple entry points.

Another example is that, given

COMMON A, B
EQUIVALENCE (B, C)

the g77 user should be able to access the variables directly, by name, without having to traverse C-like structures and unions, while f2c is unlikely to ever offer this ability (due to limitations in the C language).

However, due to apparent bugs in the back end, g77 currently doesn't take advantage of this facility at all--it doesn't emit any debugging information for `COMMON' and `EQUIVALENCE' areas, other than information on the array of `char' it creates (and, in the case of local `EQUIVALENCE', names) for each such area.

Yet another example is arrays. g77 represents them to the debugger using the same "dimensionality" as in the source code, while f2c must necessarily convert them all to one-dimensional arrays to fit into the confines of the C language. However, the level of support offered by debuggers for interactive Fortran-style access to arrays as compiled by g77 can vary widely. In some cases, it can actually be an advantage that f2c converts everything to widely supported C semantics.

In fairness, g77 could do many of the things f2c does to get things working at least as well as f2c---for now, the maintainers have tended to prefer making g77 work the way they think it is supposed to, and find help improving the other products (the GBE of gcc; gdb; and so on) to get things working properly.

Character and Hollerith Constants

To avoid the extensive hassle that would be needed to avoid this, f2c uses C character constants to encode character and Hollerith constants. That means a constant like `'HELLO'' is translated to `"hello"' in C, which further means that an extra null byte is present at the end of the constant. This null byte is superfluous.

g77 does not generate such null bytes. This represents significant savings of resources, such as on systems where `/dev/null' or `/dev/zero' represent bottlenecks in the systems' performance, because g77 simply asks for fewer zeros from the operating system than f2c.


Go to the first, previous, next, last section, table of contents.