forgot to add ffe.texi, the actual g77 front-end internals docs
From-SVN: r26518
This commit is contained in:
parent
0f4668ef1a
commit
8a19a44a1f
|
@ -0,0 +1,883 @@
|
|||
@node Front End
|
||||
@chapter Front End
|
||||
@cindex GNU Fortran Front End (FFE)
|
||||
@cindex FFE
|
||||
@cindex @code{g77}, front end
|
||||
@cindex front end, @code{g77}
|
||||
|
||||
This chapter describes some aspects of the design and implementation
|
||||
of the @code{g77} front end.
|
||||
|
||||
@menu
|
||||
* Philosophy of Code Generation::
|
||||
* Two-pass Design::
|
||||
* Challenges Posed::
|
||||
* Transforming Statements::
|
||||
* Transforming Expressions::
|
||||
@end menu
|
||||
|
||||
@node Philosophy of Code Generation
|
||||
@section Philosophy of Code Generation
|
||||
|
||||
Don't poke the bear.
|
||||
|
||||
The @code{g77} front end generates code
|
||||
via the @code{gcc} back end.
|
||||
|
||||
@cindex GNU Back End (GBE)
|
||||
@cindex GBE
|
||||
@cindex @code{gcc}, back end
|
||||
@cindex back end, gcc
|
||||
@cindex code generator
|
||||
The @code{gcc} back end (GBE) is a large, complex
|
||||
labyrinth of intricate code
|
||||
written in a combination of the C language
|
||||
and specialized languages internal to @code{gcc}.
|
||||
|
||||
While the @emph{code} that implements the GBE
|
||||
is written in a combination of languages,
|
||||
the GBE itself is,
|
||||
to the front end for a language like Fortran,
|
||||
best viewed as a @emph{compiler}
|
||||
that compiles its own, unique, language.
|
||||
|
||||
The GBE's ``source'', then, is written in this language,
|
||||
which consists primarily of
|
||||
a combination of calls to GBE functions
|
||||
and @dfn{tree} nodes
|
||||
(which are, themselves, created
|
||||
by calling GBE functions).
|
||||
|
||||
So, the @code{g77} generates code by, in effect,
|
||||
translating the Fortran code it reads
|
||||
into a form ``written'' in the ``language''
|
||||
of the @code{gcc} back end.
|
||||
|
||||
@cindex GBEL
|
||||
@cindex GNU Back End Language (GBEL)
|
||||
This language will heretofore be referred to as @dfn{GBEL},
|
||||
for GNU Back End Language.
|
||||
|
||||
GBEL is an evolving language,
|
||||
not fully specified in any published form
|
||||
as of this writing.
|
||||
It offers many facilities,
|
||||
but its ``core'' facilities
|
||||
are those that corresponding most directly
|
||||
to those needed to support @code{gcc}
|
||||
(compiling code written in GNU C).
|
||||
|
||||
The @code{g77} Fortran Front End (FFE)
|
||||
is designed and implemented
|
||||
to navigate the currents and eddies
|
||||
of ongoing GBEL and @code{gcc} development
|
||||
while also delivering on the potential
|
||||
of an integrated FFE
|
||||
(as compared to using a converter like @code{f2c}
|
||||
and feeding the output into @code{gcc}).
|
||||
|
||||
Goals of the FFE's code-generation strategy include:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
High likelihood of generation of correct code,
|
||||
or, failing that, producing a fatal diagnostic or crashing.
|
||||
|
||||
@item
|
||||
Generation of highly optimized code,
|
||||
as directed by the user
|
||||
via GBE-specific (versus @code{g77}-specific) constructs,
|
||||
such as command-line options.
|
||||
|
||||
@item
|
||||
Fast overall (FFE plus GBE) compilation.
|
||||
|
||||
@item
|
||||
Preservation of source-level debugging information.
|
||||
@end itemize
|
||||
|
||||
The strategies historically, and currently, used by the FFE
|
||||
to achieve these goals include:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Use of GBEL constructs that most faithfully encapsulate
|
||||
the semantics of Fortran.
|
||||
|
||||
@item
|
||||
Avoidance of GBEL constructs that are so rarely used,
|
||||
or limited to use in specialized situations not related to Fortran,
|
||||
that their reliability and performance has not yet been established
|
||||
as sufficient for use by the FFE.
|
||||
|
||||
@item
|
||||
Flexible design, to readily accommodate changes to specific
|
||||
code-generation strategies, perhaps governed by command-line options.
|
||||
@end itemize
|
||||
|
||||
@cindex Bear-poking
|
||||
@cindex Poking the bear
|
||||
``Don't poke the bear'' somewhat summarizes the above strategies.
|
||||
The GBE is the bear.
|
||||
The FFE is designed and implemented to avoid poking it
|
||||
in ways that are likely to just annoy it.
|
||||
The FFE usually either tackles it head-on,
|
||||
or avoids treating it in ways dissimilar to how
|
||||
the @code{gcc} front end treats it.
|
||||
|
||||
For example, the FFE uses the native array facility in the back end
|
||||
instead of the lower-level pointer-arithmetic facility
|
||||
used by @code{gcc} when compiling @code{f2c} output).
|
||||
Theoretically, this presents more opportunities for optimization,
|
||||
faster compile times,
|
||||
and the production of more faithful debugging information.
|
||||
These benefits were not, however, immediately realized,
|
||||
mainly because @code{gcc} itself makes little or no use
|
||||
of the native array facility.
|
||||
|
||||
Complex arithmetic is a case study of the evolution of this strategy.
|
||||
When originally implemented,
|
||||
the GBEL had just evolved its own native complex-arithmetic facility,
|
||||
so the FFE took advantage of that.
|
||||
|
||||
When porting @code{g77} to 64-bit systems,
|
||||
it was discovered that the GBE didn't really
|
||||
implement its native complex-arithmetic facility properly.
|
||||
|
||||
The short-term solution was to rewrite the FFE
|
||||
to instead use the lower-level facilities
|
||||
that'd be used by @code{gcc}-compiled code
|
||||
(assuming that code, itself, didn't use the native complex type
|
||||
provided, as an extension, by @code{gcc}),
|
||||
since these were known to work,
|
||||
and, in any case, if shown to not work,
|
||||
would likely be rapidly fixed
|
||||
(since they'd likely not work for vanilla C code in similar circumstances).
|
||||
|
||||
However, the rewrite accommodated the original, native approach as well
|
||||
by offering a command-line option to select it over the emulated approach.
|
||||
This allowed users, and especially GBE maintainers, to try out
|
||||
fixes to complex-arithmetic support in the GBE
|
||||
while @code{g77} continued to default to compiling more code correctly,
|
||||
albeit producing (typically) slower executables.
|
||||
|
||||
As of April 1999, it appeared that the last few bugs
|
||||
in the GBE's support of its native complex-arithmetic facility
|
||||
were worked out.
|
||||
The FFE was changed back to default to using that native facility,
|
||||
leaving emulation as an option.
|
||||
|
||||
Other Fortran constructs---arrays, character strings,
|
||||
complex division, @code{COMMON} and @code{EQUIVALENCE} aggregates,
|
||||
and so on---involve issues similar to those pertaining to complex arithmetic.
|
||||
|
||||
So, it is possible that the history
|
||||
of how the FFE handled complex arithmetic
|
||||
will be repeated, probably in modified form
|
||||
(and hopefully over shorter timeframes),
|
||||
for some of these other facilities.
|
||||
|
||||
@node Two-pass Design
|
||||
@section Two-pass Design
|
||||
|
||||
The FFE does not tell the GBE anything about a program unit
|
||||
until after the last statement in that unit has been parsed.
|
||||
(A program unit is a Fortran concept that corresponds, in the C world,
|
||||
mostly closely to functions definitions in ISO C.
|
||||
That is, a program unit in Fortran is like a top-level function in C.
|
||||
Nested functions, found among the extensions offered by GNU C,
|
||||
correspond roughly to Fortran's statement functions.)
|
||||
|
||||
So, while parsing the code in a program unit,
|
||||
the FFE saves up all the information
|
||||
on statements, expressions, names, and so on,
|
||||
until it has seen the last statement.
|
||||
|
||||
At that point, the FFE revisits the saved information
|
||||
(in what amounts to a second @dfn{pass} over the program unit)
|
||||
to perform the actual translation of the program unit into GBEL,
|
||||
ultimating in the generation of assembly code for it.
|
||||
|
||||
Some lookahead is performed during this second pass,
|
||||
so the FFE could be viewed as a ``two-plus-pass'' design.
|
||||
|
||||
@menu
|
||||
* Two-pass Code::
|
||||
* Why Two Passes::
|
||||
@end menu
|
||||
|
||||
@node Two-pass Code
|
||||
@subsection Two-pass Code
|
||||
|
||||
Most of the code that turns the first pass (parsing)
|
||||
into a second pass for code generation
|
||||
is in @file{@value{path-g77}/std.c}.
|
||||
|
||||
It has external functions,
|
||||
called mainly by siblings in @file{@value{path-g77}/stc.c},
|
||||
that record the information on statements and expressions
|
||||
in the order they are seen in the source code.
|
||||
These functions save that information.
|
||||
|
||||
It also has an external function that revisits that information,
|
||||
calling the siblings in @file{@value{path-g77}/ste.c},
|
||||
which handles the actual code generation
|
||||
(by generating GBEL code,
|
||||
that is, by calling GBE routines
|
||||
to represent and specify expressions, statements, and so on).
|
||||
|
||||
@node Why Two Passes
|
||||
@subsection Why Two Passes
|
||||
|
||||
The need for two passes was not immediately evident
|
||||
during the design and implementation of the code in the FFE
|
||||
that was to produce GBEL.
|
||||
Only after a few kludges,
|
||||
to handle things like incorrectly-guessed @code{ASSIGN} label nature,
|
||||
had been implemented,
|
||||
did enough evidence pile up to make it clear
|
||||
that @file{std.c} had to be introduced to intercept,
|
||||
save, then revisit as part of a second pass,
|
||||
the digested contents of a program unit.
|
||||
|
||||
Other such missteps have occurred during the evolution of the FFE,
|
||||
because of the different goals of the FFE and the GBE.
|
||||
|
||||
Because the GBE's original, and still primary, goal
|
||||
was to directly support the GNU C language,
|
||||
the GBEL, and the GBE itself,
|
||||
requires more complexity
|
||||
on the part of most front ends
|
||||
than it requires of @code{gcc}'s.
|
||||
|
||||
For example,
|
||||
the GBEL offers an interface that permits the @code{gcc} front end
|
||||
to implement most, or all, of the language features it supports,
|
||||
without the front end having to
|
||||
make use of non-user-defined variables.
|
||||
(It's almost certainly the case that all of K&R C,
|
||||
and probably ANSI C as well,
|
||||
is handled by the @code{gcc} front end
|
||||
without declaring such variables.)
|
||||
|
||||
The FFE, on the other hand, must resort to a variety of ``tricks''
|
||||
to achieve its goals.
|
||||
|
||||
Consider the following C code:
|
||||
|
||||
@smallexample
|
||||
int
|
||||
foo (int a, int b)
|
||||
@{
|
||||
int c = 0;
|
||||
|
||||
if ((c = bar (c)) == 0)
|
||||
goto done;
|
||||
|
||||
quux (c << 1);
|
||||
|
||||
done:
|
||||
return c;
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
Note what kinds of objects are declared, or defined, before their use,
|
||||
and before any actual code generation involving them
|
||||
would normally take place:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Return type of function
|
||||
|
||||
@item
|
||||
Entry point(s) of function
|
||||
|
||||
@item
|
||||
Dummy arguments
|
||||
|
||||
@item
|
||||
Variables
|
||||
|
||||
@item
|
||||
Initial values for variables
|
||||
@end itemize
|
||||
|
||||
Whereas, the following items can, and do,
|
||||
suddenly appear ``out of the blue'' in C:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Label references
|
||||
|
||||
@item
|
||||
Function references
|
||||
@end itemize
|
||||
|
||||
Not surprisingly, the GBE faithfully permits the latter set of items
|
||||
to be ``discovered'' partway through GBEL ``programs'',
|
||||
just as they are permitted to in C.
|
||||
|
||||
Yet, the GBE has tended, at least in the past,
|
||||
to be reticent to fully support similar ``late'' discovery
|
||||
of items in the former set.
|
||||
|
||||
This makes Fortran a poor fit for the ``safe'' subset of GBEL.
|
||||
Consider:
|
||||
|
||||
@smallexample
|
||||
FUNCTION X (A, ARRAY, ID1)
|
||||
CHARACTER*(*) A
|
||||
DOUBLE PRECISION X, Y, Z, TMP, EE, PI
|
||||
REAL ARRAY(ID1*ID2)
|
||||
COMMON ID2
|
||||
EXTERNAL FRED
|
||||
|
||||
ASSIGN 100 TO J
|
||||
CALL FOO (I)
|
||||
IF (I .EQ. 0) PRINT *, A(0)
|
||||
GOTO 200
|
||||
|
||||
ENTRY Y (Z)
|
||||
ASSIGN 101 TO J
|
||||
200 PRINT *, A(1)
|
||||
READ *, TMP
|
||||
GOTO J
|
||||
100 X = TMP * EE
|
||||
RETURN
|
||||
101 Y = TMP * PI
|
||||
CALL FRED
|
||||
DATA EE, PI /2.71D0, 3.14D0/
|
||||
END
|
||||
@end smallexample
|
||||
|
||||
Here are some observations about the above code,
|
||||
which, while somewhat contrived,
|
||||
conforms to the FORTRAN 77 and Fortran 90 standards:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
The return type of function @samp{X} is not known
|
||||
until the @samp{DOUBLE PRECISION} line has been parsed.
|
||||
|
||||
@item
|
||||
Whether @samp{A} is a function or a variable
|
||||
is not known until the @samp{PRINT *, A(0)} statement
|
||||
has been parsed.
|
||||
|
||||
@item
|
||||
The bounds of the array of argument @samp{ARRAY}
|
||||
depend on a computation involving
|
||||
the subsequent argument @samp{ID1}
|
||||
and the blank-common member @samp{ID2}.
|
||||
|
||||
@item
|
||||
Whether @samp{Y} and @samp{Z} are local variables,
|
||||
additional function entry points,
|
||||
or dummy arguments to additional entry points
|
||||
is not known
|
||||
until the @samp{ENTRY} statement is parsed.
|
||||
|
||||
@item
|
||||
Similarly, whether @samp{TMP} is a local variable is not known
|
||||
until the @samp{READ *, TMP} statement is parsed.
|
||||
|
||||
@item
|
||||
The initial values for @samp{EE} and @samp{PI}
|
||||
are not known until after the @samp{DATA} statement is parsed.
|
||||
|
||||
@item
|
||||
Whether @samp{FRED} is a function returning type @code{REAL}
|
||||
or a subroutine
|
||||
(which can be thought of as returning type @code{void}
|
||||
@emph{or}, to support alternate returns in a simple way,
|
||||
type @code{int})
|
||||
is not known
|
||||
until the @samp{CALL FRED} statement is parsed.
|
||||
|
||||
@item
|
||||
Whether @samp{100} is a @code{FORMAT} label
|
||||
or the label of an executable statement
|
||||
is not known
|
||||
until the @samp{X =} statement is parsed.
|
||||
(These two types of labels get @emph{very} different treatment,
|
||||
especially when @code{ASSIGN}'ed.)
|
||||
|
||||
@item
|
||||
That @samp{J} is a local variable is not known
|
||||
until the first @samp{ASSIGN} statement is parsed.
|
||||
(This happens @emph{after} executable code has been seen.)
|
||||
@end itemize
|
||||
|
||||
Very few of these ``discoveries''
|
||||
can be accommodated by the GBE as it has evolved over the years.
|
||||
The GBEL doesn't support several of them,
|
||||
and those it might appear to support
|
||||
don't always work properly,
|
||||
especially in combination with other GBEL and GBE features,
|
||||
as implemented in the GBE.
|
||||
|
||||
(Had the GBE and its GBEL originally evolved to support @code{g77},
|
||||
the shoe would be on the other foot, so to speak---most, if not all,
|
||||
of the above would be directly supported by the GBEL,
|
||||
and a few C constructs would probably not, as they are in reality,
|
||||
be supported.
|
||||
Both this mythical, and today's real, GBE caters to its GBEL
|
||||
by, sometimes, scrambling around, cleaning up after itself---after
|
||||
discovering that assumptions it made earlier during code generation
|
||||
are incorrect.)
|
||||
|
||||
So, the FFE handles these discrepancies---between the order in which
|
||||
it discovers facts about the code it is compiling,
|
||||
and the order in which the GBEL and GBE support such discoveries---by
|
||||
performing what amounts to two
|
||||
passes over each program unit.
|
||||
|
||||
(A few ambiguities can remain at that point,
|
||||
such as whether, given @samp{EXTERNAL BAZ}
|
||||
and no other reference to @samp{BAZ} in the program unit,
|
||||
it is a subroutine, a function, or a block-data---which, in C-speak,
|
||||
governs its declared return type.
|
||||
Fortunately, these distinctions are easily finessed
|
||||
for the procedure, library, and object-file interfaces
|
||||
supported by @code{g77}.)
|
||||
|
||||
@node Challenges Posed
|
||||
@section Challenges Posed
|
||||
|
||||
Consider the following Fortran code, which uses various extensions
|
||||
(including some to Fortran 90):
|
||||
|
||||
@smallexample
|
||||
SUBROUTINE X(A)
|
||||
CHARACTER*(*) A
|
||||
COMPLEX CFUNC
|
||||
INTEGER*2 CLOCKS(200)
|
||||
INTEGER IFUNC
|
||||
|
||||
CALL SYSTEM_CLOCK (CLOCKS (IFUNC (CFUNC ('('//A//')'))))
|
||||
@end smallexample
|
||||
|
||||
The above poses the following challenges to any Fortran compiler
|
||||
that uses run-time interfaces, and a run-time library, roughly similar
|
||||
to those used by @code{g77}:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Assuming the library routine that supports @code{SYSTEM_CLOCK}
|
||||
expects to set an @code{INTEGER*4} variable via its @code{COUNT} argument,
|
||||
the compiler must make available to it a temporary variable of that type.
|
||||
|
||||
@item
|
||||
Further, after the @code{SYSTEM_CLOCK} library routine returns,
|
||||
the compiler must ensure that the temporary variable it wrote
|
||||
is copied into the appropriate element of the @samp{CLOCKS} array.
|
||||
(This assumes the compiler doesn't just reject the code,
|
||||
which it should if it is compiling under some kind of a "strict" option.)
|
||||
|
||||
@item
|
||||
To determine the correct index into the @samp{CLOCKS} array,
|
||||
(putting aside the fact that the index, in this particular case,
|
||||
need not be computed until after
|
||||
the @code{SYSTEM_CLOCK} library routine returns),
|
||||
the compiler must ensure that the @code{IFUNC} function is called.
|
||||
|
||||
That requires evaluating its argument,
|
||||
which requires, for @code{g77}
|
||||
(assuming @code{-ff2c} is in force),
|
||||
reserving a temporary variable of type @code{COMPLEX}
|
||||
for use as a repository for the return value
|
||||
being computed by @samp{CFUNC}.
|
||||
|
||||
@item
|
||||
Before invoking @samp{CFUNC},
|
||||
is argument must be evaluated,
|
||||
which requires allocating, at run time,
|
||||
a temporary large enough to hold the result of the concatenation,
|
||||
as well as actually performing the concatenation.
|
||||
|
||||
@item
|
||||
The large temporary needed during invocation of @code{CFUNC}
|
||||
should, ideally, be deallocated
|
||||
(or, at least, left to the GBE to dispose of, as it sees fit)
|
||||
as soon as @code{CFUNC} returns,
|
||||
which means before @code{IFUNC} is called
|
||||
(as it might need a lot of dynamically allocated memory).
|
||||
@end itemize
|
||||
|
||||
@code{g77} currently doesn't support all of the above,
|
||||
but, so that it might someday, it has evolved to handle
|
||||
at least some of the above requirements.
|
||||
|
||||
Meeting the above requirements is made more challenging
|
||||
by conforming to the requirements of the GBEL/GBE combination.
|
||||
|
||||
@node Transforming Statements
|
||||
@section Transforming Statements
|
||||
|
||||
Most Fortran statements are given their own block,
|
||||
and, for temporary variables they might need, their own scope.
|
||||
(A block is what distinguishes @samp{@{ foo (); @}}
|
||||
from just @samp{foo ();} in C.
|
||||
A scope is included with every such block,
|
||||
providing a distinct name space for local variables.)
|
||||
|
||||
Label definitions for the statement precede this block,
|
||||
so @samp{10 PRINT *, I} is handled more like
|
||||
@samp{fl10: @{ @dots{} @}} than @samp{@{ fl10: @dots{} @}}
|
||||
(where @samp{fl10} is just a notation meaning ``Fortran Label 10''
|
||||
for the purposes of this document).
|
||||
|
||||
@menu
|
||||
* Statements Needing Temporaries::
|
||||
* Transforming DO WHILE::
|
||||
* Transforming Iterative DO::
|
||||
* Transforming Block IF::
|
||||
* Transforming SELECT CASE::
|
||||
@end menu
|
||||
|
||||
@node Statements Needing Temporaries
|
||||
@subsection Statements Needing Temporaries
|
||||
|
||||
Any temporaries needed during, but not beyond,
|
||||
execution of a Fortran statement,
|
||||
are made local to the scope of that statement's block.
|
||||
|
||||
This allows the GBE to share storage for these temporaries
|
||||
among the various statements without the FFE
|
||||
having to manage that itself.
|
||||
|
||||
(The GBE could, of course, decide to optimize
|
||||
management of these temporaries.
|
||||
For example, it could, theoretically,
|
||||
schedule some of the computations involving these temporaries
|
||||
to occur in parallel.
|
||||
More practically, it might leave the storage for some temporaries
|
||||
``live'' beyond their scopes, to reduce the number of
|
||||
manipulations of the stack pointer at run time.)
|
||||
|
||||
Temporaries needed across distinct statement boundaries usually
|
||||
are associated with Fortran blocks (such as @code{DO}/@code{END DO}).
|
||||
(Also, there might be temporaries not associated with blocks at all---these
|
||||
would be in the scope of the entire program unit.)
|
||||
|
||||
Each Fortran block @emph{should} get its own block/scope in the GBE.
|
||||
This is best, because it allows temporaries to be more naturally handled.
|
||||
However, it might pose problems when handling labels
|
||||
(in particular, when they're the targets of @code{GOTO}s outside the Fortran
|
||||
block), and generally just hassling with replicating
|
||||
parts of the @code{gcc} front end
|
||||
(because the FFE needs to support
|
||||
an arbitrary number of nested back-end blocks
|
||||
if each Fortran block gets one).
|
||||
|
||||
So, there might still be a need for top-level temporaries, whose
|
||||
``owning'' scope is that of the containing procedure.
|
||||
|
||||
Also, there seems to be problems declaring new variables after
|
||||
generating code (within a block) in the back end, leading to, e.g.,
|
||||
@samp{label not defined before binding contour} or similar messages,
|
||||
when compiling with @samp{-fstack-check} or
|
||||
when compiling for certain targets.
|
||||
|
||||
Because of that, and because sometimes these temporaries are not
|
||||
discovered until in the middle of of generating code for an expression
|
||||
statement (as in the case of the optimization for @samp{X**I}),
|
||||
it seems best to always
|
||||
pre-scan all the expressions that'll be expanded for a block
|
||||
before generating any of the code for that block.
|
||||
|
||||
This pre-scan then handles discovering and declaring, to the back end,
|
||||
the temporaries needed for that block.
|
||||
|
||||
It's also important to treat distinct items in an I/O list as distinct
|
||||
statements deserving their own blocks.
|
||||
That's because there's a requirement
|
||||
that each I/O item be fully processed before the next one,
|
||||
which matters in cases like @samp{READ (*,*), I, A(I)}---the
|
||||
element of @samp{A} read in the second item
|
||||
@emph{must} be determined from the value
|
||||
of @samp{I} read in the first item.
|
||||
|
||||
@node Transforming DO WHILE
|
||||
@subsection Transforming DO WHILE
|
||||
|
||||
@samp{DO WHILE(expr)} @emph{must} be implemented
|
||||
so that temporaries needed to evaluate @samp{expr}
|
||||
are generated just for the test, each time.
|
||||
|
||||
Consider how @samp{DO WHILE (A//B .NE. 'END'); @dots{}; END DO} is transformed:
|
||||
|
||||
@smallexample
|
||||
for (;;)
|
||||
@{
|
||||
int temp0;
|
||||
|
||||
@{
|
||||
char temp1[large];
|
||||
|
||||
libg77_catenate (temp1, a, b);
|
||||
temp0 = libg77_ne (temp1, 'END');
|
||||
@}
|
||||
|
||||
if (! temp0)
|
||||
break;
|
||||
|
||||
@dots{}
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
In this case, it seems like a time/space tradeoff
|
||||
between allocating and deallocating @samp{temp1} for each iteration
|
||||
and allocating it just once for the entire loop.
|
||||
|
||||
However, if @samp{temp1} is allocated just once for the entire loop,
|
||||
it could be the wrong size for subsequent iterations of that loop
|
||||
in cases like @samp{DO WHILE (A(I:J)//B .NE. 'END')},
|
||||
because the body of the loop might modify @samp{I} or @samp{J}.
|
||||
|
||||
So, the above implementation is used,
|
||||
though a more optimal one can be used
|
||||
in specific circumstances.
|
||||
|
||||
@node Transforming Iterative DO
|
||||
@subsection Transforming Iterative DO
|
||||
|
||||
An iterative @code{DO} loop
|
||||
(one that specifies an iteration variable)
|
||||
is required by the Fortran standards
|
||||
to be implemented as though an iteration count
|
||||
is computed before entering the loop body,
|
||||
and that iteration count used to determine
|
||||
the number of times the loop body is to be performed
|
||||
(assuming the loop isn't cut short via @code{GOTO} or @code{EXIT}).
|
||||
|
||||
The FFE handles this by allocating a temporary variable
|
||||
to contain the computed number of iterations.
|
||||
Since this variable must be in a scope that includes the entire loop,
|
||||
a GBEL block is created for that loop,
|
||||
and the variable declared as belonging to the scope of that block.
|
||||
|
||||
@node Transforming Block IF
|
||||
@subsection Transforming Block IF
|
||||
|
||||
Consider:
|
||||
|
||||
@smallexample
|
||||
SUBROUTINE X(A,B,C)
|
||||
CHARACTER*(*) A, B, C
|
||||
LOGICAL LFUNC
|
||||
|
||||
IF (LFUNC (A//B)) THEN
|
||||
CALL SUBR1
|
||||
ELSE IF (LFUNC (A//C)) THEN
|
||||
CALL SUBR2
|
||||
ELSE
|
||||
CALL SUBR3
|
||||
END
|
||||
@end smallexample
|
||||
|
||||
The arguments to the two calls to @samp{LFUNC}
|
||||
require dynamic allocation (at run time),
|
||||
but are not required during execution of the @samp{CALL} statements.
|
||||
|
||||
So, the scopes of those temporaries must be within blocks inside
|
||||
the block corresponding to the Fortran @code{IF} block.
|
||||
|
||||
This cannot be represented ``naturally''
|
||||
in vanilla C, nor in GBEL.
|
||||
The @samp{if}, @samp{elseif}, @samp{else},
|
||||
and @samp{endif} constructs
|
||||
provided by both languages must,
|
||||
for a given @samp{if} block,
|
||||
share the same C/GBE block.
|
||||
|
||||
Therefore, any temporaries needed during evaluation of @samp{expr}
|
||||
while executing @samp{ELSE IF(expr)}
|
||||
must either have been predeclared
|
||||
at the top of the corresponding @code{IF} block,
|
||||
or declared within a new block for that @code{ELSE IF}---a block that,
|
||||
since it cannot contain the @code{else} or @code{else if} itself
|
||||
(due to the above requirement),
|
||||
actually implements the rest of the @code{IF} block's
|
||||
@code{ELSE IF} and @code{ELSE} statements
|
||||
within an inner block.
|
||||
|
||||
The FFE takes the latter approach.
|
||||
|
||||
@node Transforming SELECT CASE
|
||||
@subsection Transforming SELECT CASE
|
||||
|
||||
@code{SELECT CASE} poses a few interesting problems for code generation,
|
||||
if efficiency and frugal stack management are important.
|
||||
|
||||
Consider @samp{SELECT CASE (I('PREFIX'//A))},
|
||||
where @samp{A} is @code{CHARACTER*(*)}.
|
||||
In a case like this---basically,
|
||||
in any case where largish temporaries are needed
|
||||
to evaluate the expression---those temporaries should
|
||||
not be ``live'' during execution of any of the @code{CASE} blocks.
|
||||
|
||||
So, evaluation of the expression is best done within its own block,
|
||||
which in turn is within the @code{SELECT CASE} block itself
|
||||
(which contains the code for the CASE blocks as well,
|
||||
though each within their own block).
|
||||
|
||||
Otherwise, we'd have the rough equivalent of this pseudo-code:
|
||||
|
||||
@smallexample
|
||||
@{
|
||||
char temp[large];
|
||||
|
||||
libg77_catenate (temp, 'prefix', a);
|
||||
|
||||
switch (i (temp))
|
||||
@{
|
||||
case 0:
|
||||
@dots{}
|
||||
@}
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
And that would leave temp[large] in scope during the CASE blocks
|
||||
(although a clever back end *could* see that it isn't referenced
|
||||
in them, and thus free that temp before executing the blocks).
|
||||
|
||||
So this approach is used instead:
|
||||
|
||||
@smallexample
|
||||
@{
|
||||
int temp0;
|
||||
|
||||
@{
|
||||
char temp1[large];
|
||||
|
||||
libg77_catenate (temp1, 'prefix', a);
|
||||
temp0 = i (temp1);
|
||||
@}
|
||||
|
||||
switch (temp0)
|
||||
@{
|
||||
case 0:
|
||||
@dots{}
|
||||
@}
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
Note how @samp{temp1} goes out of scope before starting the switch,
|
||||
thus making it easy for a back end to free it.
|
||||
|
||||
The problem @emph{that} solution has, however,
|
||||
is with @samp{SELECT CASE('prefix'//A)}
|
||||
(which is currently not supported).
|
||||
|
||||
Unless the GBEL is extended to support arbitrarily long character strings
|
||||
in its @code{case} facility,
|
||||
the FFE has to implement @code{SELECT CASE} on @code{CHARACTER}
|
||||
(probably excepting @code{CHARACTER*1})
|
||||
using a cascade of
|
||||
@code{if}, @code{elseif}, @code{else}, and @code{endif} constructs
|
||||
in GBEL.
|
||||
|
||||
To prevent the (potentially large) temporary,
|
||||
needed to hold the selected expression itself (@samp{'prefix'//A}),
|
||||
from being in scope during execution of the @code{CASE} blocks,
|
||||
two approaches are available:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Pre-evaluate all the @code{CASE} tests,
|
||||
producing an integer ordinal that is used,
|
||||
a la @samp{temp0} in the earlier example,
|
||||
as if @samp{SELECT CASE(temp0)} had been written.
|
||||
|
||||
Each corresponding @code{CASE} is replaced with @samp{CASE(@var{i})},
|
||||
where @var{i} is the ordinal for that case,
|
||||
determined while, or before,
|
||||
generating the cascade of @samp{if}-related constructs
|
||||
to cope with @code{CHARACTER} selection.
|
||||
|
||||
@item
|
||||
Make @samp{temp0} above just
|
||||
large enough to hold the longest @code{CASE} string
|
||||
that'll actually be compared against the expression
|
||||
(in this case, @samp{'prefix'//A}).
|
||||
|
||||
Since that length must be constant
|
||||
(because @code{CASE} expressions are all constant),
|
||||
it won't be so large,
|
||||
and, further, @samp{temp1} need not be dynamically allocated,
|
||||
since normal @code{CHARACTER} assignment can be used
|
||||
into the fixed-length @samp{temp0}.
|
||||
@end itemize
|
||||
|
||||
Both of these solutions require @code{SELECT CASE} implementation
|
||||
to be changed so all the corresponding @code{CASE} statements
|
||||
are seen during the actual code generation for @code{SELECT CASE}.
|
||||
|
||||
@node Transforming Expressions
|
||||
@section Transforming Expressions
|
||||
|
||||
The interactions between statements, expressions, and subexpressions
|
||||
at program run time can be viewed as:
|
||||
|
||||
@smallexample
|
||||
@var{action}(@var{expr})
|
||||
@end smallexample
|
||||
|
||||
Here, @var{action} is the series of steps
|
||||
performed to effect the statement,
|
||||
and @var{expr} is the expression
|
||||
whose value is used by @var{action}.
|
||||
|
||||
Expanding the above shows a typical order of events at run time:
|
||||
|
||||
@smallexample
|
||||
Evaluate @var{expr}
|
||||
Perform @var{action}, using result of evaluation of @var{expr}
|
||||
Clean up after evaluating @var{expr}
|
||||
@end smallexample
|
||||
|
||||
So, if evaluating @var{expr} requires allocating memory,
|
||||
that memory can be freed before performing @var{action}
|
||||
only if it is not needed to hold the result of evaluating @var{expr}.
|
||||
Otherwise, it must be freed no sooner than
|
||||
after @var{action} has been performed.
|
||||
|
||||
The above are recursive definitions,
|
||||
in the sense that they apply to subexpressions of @var{expr}.
|
||||
|
||||
That is, evaluating @var{expr} involves
|
||||
evaluating all of its subexpressions,
|
||||
performing the @var{action} that computes the
|
||||
result value of @var{expr},
|
||||
then cleaning up after evaluating those subexpressions.
|
||||
|
||||
The recursive nature of this evaluation is implemented
|
||||
via recursive-descent transformation of the top-level statements,
|
||||
their expressions, @emph{their} subexpressions, and so on.
|
||||
|
||||
However, that recursive-descent transformation is,
|
||||
due to the nature of the GBEL,
|
||||
focused primarily on generating a @emph{single} stream of code
|
||||
to be executed at run time.
|
||||
|
||||
Yet, from the above, it's clear that multiple streams of code
|
||||
must effectively be simultaneously generated
|
||||
during the recursive-descent analysis of statements.
|
||||
|
||||
The primary stream implements the primary @var{action} items,
|
||||
while at least two other streams implement
|
||||
the evaluation and clean-up items.
|
||||
|
||||
Requirements imposed by expressions include:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Whether the caller needs to have a temporary ready
|
||||
to hold the value of the expression.
|
||||
|
||||
@item
|
||||
Other stuff???
|
||||
|
||||
|
||||
@end itemize
|
Loading…
Reference in New Issue