Next: Alternate Returns, Previous: Adjustable Arrays, Up: Debugging and Interfacing
The GBE does not understand the general concept of
alternate entry points as Fortran provides via the ENTRY statement.
g77 gets around this by using an approach to compiling procedures
having at least one ENTRY
statement that is almost identical to the
approach used by f2c.
(An alternate approach could be used that
would probably generate faster, but larger, code that would also
be a bit easier to debug.)
Information on how g77 implements ENTRY
is provided for those
trying to debug such code.
The choice of implementation seems
unlikely to affect code (compiled in other languages) that interfaces
to such code.
g77 compiles exactly one public procedure for the primary entry
point of a procedure plus each ENTRY
point it specifies, as usual.
That is, in terms of the public interface, there is no difference
between
SUBROUTINE X END SUBROUTINE Y END
and:
SUBROUTINE X ENTRY Y END
The difference between the above two cases lies in the code compiled for the `X' and `Y' procedures themselves, plus the fact that, for the second case, an extra internal procedure is compiled.
For every Fortran procedure with at least one ENTRY
statement, g77 compiles an extra procedure
named `__g77_masterfun_x', where x is
the name of the primary entry point (which, in the above case,
using the standard compiler options, would be `x_' in C).
This extra procedure is compiled as a private procedure—that is, a procedure not accessible by name to separately compiled modules. It contains all the code in the program unit, including the code for the primary entry point plus for every entry point. (The code for each public procedure is quite short, and explained later.)
The extra procedure has some other interesting characteristics.
The argument list for this procedure is invented by g77.
It contains
a single integer argument named `__g77_which_entrypoint',
passed by value (as in Fortran's `%VAL()' intrinsic), specifying the
entry point index—0 for the primary entry point, 1 for the
first entry point (the first ENTRY
statement encountered), 2 for
the second entry point, and so on.
It also contains, for functions returning CHARACTER
and
(when -ff2c is in effect) COMPLEX
functions,
and for functions returning different types among the
ENTRY
statements (e.g. `REAL FUNCTION R()'
containing `ENTRY I()'), an argument named `__g77_result' that
is expected at run time to contain a pointer to where to store
the result of the entry point.
For CHARACTER
functions, this
storage area is an array of the appropriate number of characters;
for COMPLEX
functions, it is the appropriate area for the return
type; for multiple-return-type functions, it is a union of all the supported return
types (which cannot include CHARACTER
, since combining CHARACTER
and non-CHARACTER
return types via ENTRY
in a single function
is not supported by g77).
For CHARACTER
functions, the `__g77_result' argument is followed
by yet another argument named `__g77_length' that, at run time,
specifies the caller's expected length of the returned value.
Note that only CHARACTER*(*)
functions and entry points actually
make use of this argument, even though it is always passed by
all callers of public CHARACTER
functions (since the caller does not
generally know whether such a function is CHARACTER*(*)
or whether
there are any other callers that don't have that information).
The rest of the argument list is the union of all the arguments
specified for all the entry points (in their usual forms, e.g.
CHARACTER
arguments have extra length arguments, all appended at
the end of this list).
This is considered the “master list” of
arguments.
The code for this procedure has, before the code for the first executable statement, code much like that for the following Fortran statement:
GOTO (100000,100001,100002), __g77_which_entrypoint 100000 ...code for primary entry point... 100001 ...code immediately following first ENTRY statement... 100002 ...code immediately following second ENTRY statement...
(Note that invalid Fortran statement labels and variable names are used in the above example to highlight the fact that it represents code generated by the g77 internals, not code to be written by the user.)
It is this code that, when the procedure is called, picks which entry point to start executing.
Getting back to the public procedures (`x' and `Y' in the original
example), those procedures are fairly simple.
Their interfaces
are just like they would be if they were self-contained procedures
(without ENTRY
), of course, since that is what the callers
expect.
Their code consists of simply calling the private
procedure, described above, with the appropriate extra arguments
(the entry point index, and perhaps a pointer to a multiple-type-
return variable, local to the public procedure, that contains
all the supported returnable non-character types).
For arguments
that are not listed for a given entry point that are listed for
other entry points, and therefore that are in the “master list”
for the private procedure, null pointers (in C, the NULL
macro)
are passed.
Also, for entry points that are part of a multiple-type-
returning function, code is compiled after the call of the private
procedure to extract from the multi-type union the appropriate result,
depending on the type of the entry point in question, returning
that result to the original caller.
When debugging a procedure containing alternate entry points, you can either set a break point on the public procedure itself (e.g. a break point on `X' or `Y') or on the private procedure that contains most of the pertinent code (e.g. `__g77_masterfun_x'). If you do the former, you should use the debugger's command to “step into” the called procedure to get to the actual code; with the latter approach, the break point leaves you right at the actual code, skipping over the public entry point and its call to the private procedure (unless you have set a break point there as well, of course).
Further, the list of dummy arguments that is visible when the private procedure is active is going to be the expanded version of the list for whichever particular entry point is active, as explained above, and the way in which return values are handled might well be different from how they would be handled for an equivalent single-entry function.