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 DO
/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 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 GOTO
s outside the Fortran
block), and generally just hassling with replicating
parts of the 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.,
label not defined before binding contour
or similar messages,
when compiling with -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 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 READ (*,*), I, A(I)
--the
element of A
read in the second item
must be determined from the value
of I
read in the first item.