This is the mail archive of the
fortran@gcc.gnu.org
mailing list for the GNU Fortran project.
Re: Function Inlining for FORTRAN
- From: Paul Brook <paul at codesourcery dot com>
- To: fortran at gcc dot gnu dot org,Canqun Yang <canqun at nudt dot edu dot cn>
- Cc: jh at suse dot cz,gcc at gcc dot gnu dot org,Steven Bosscher <stevenb at suse dot de>
- Date: Wed, 20 Jul 2005 16:22:53 +0100
- Subject: Re: Function Inlining for FORTRAN
- Organization: CodeSourcery
- References: <20050720143531.A56FA53C536@ds20.nudt.edu.cn>
On Wednesday 20 July 2005 15:35, Canqun Yang wrote:
> Hi, all
>
> Function inlining for FORTRAN programs always fails.
Not entirely true. Inlining of contained procedures works fine (or it did last
time I checked). This should include inlining of siblings within a module.
> If no one engages in it, I will give a try. Would you please give me
> some clues?
The problem is that each top level program unit (PU)[1] is compiled
separately. Each PU has it's own "external" decls for all function calls,
even if the function happens to be in the same function. Thus each PU is an
isolated self-contained tree structure, and the callgraph doesn't know the
definition and declaration are actually the same thing.
Basically what you need to do is parse the whole file, then start generating
code.
Unfortunately this isn't simple (or it would have been fixed already!).
Unlike C Fortran doesn't have file-level scope. It makes absolutely no
difference whether two procedures are in the same file, or in different
files. You get all the problems that multifile IPA in C experiences within a
single Fortran file.
The biggest problem is type consistency and aliasing. Consider the following
example:
subroutine a(p)
type t
integer :: t1
end type
type(t) :: p
p%t1 = 42
end subroutine
subroutine b
type u
integer :: u1
end type
type (u) :: q
call a(q)
print * q%u1
end subroutine
Here you have two different derived types which are actually the same derived
types. To make unit-at-a-time work (ie. inlining) you need to either
(a) Replace all occurrences of one type with the other
(b) Tell the compiler that the two types alias, and fixup the types with
explicit casts at any interfaces.
Ideally we'd do (a), but I don't think doing that is practical, and might not
even be possible. I think it would require fairly major hacking of the whole
frontend.
Which leaves (b).
Currently the high-level flow is something like:
- Parse and resolve the PU. This is basically everything before
gfc_generate_code.
- Call gfc_generate_code (or gfc_generate_module_code) to generate code for
that PU.
- Throw away the PU.
- Repeat for each function.
To implement (b) this needs to be changed to:
- Do everything up until gfc_generate{,_module}_code as normal.
- Save the results somewhere and repeat for each PU.
- Identify calls for procedures for which we have definitions, and link them
together somehow. It 's probably worth maintaining some sort of global symbol
table and building these associations incrementally during resolution.
- Generate global DECLs for all PU. Something similar to
gfc_create_function_decl. Probably also generate global DECLs for external
routines.
- Generate common blocks. This may simplify the existing code because we have
all definitions before we start generating DECLs.
- Generate code for each PU. This is more-or-less the same as the current code
except function calls may need explicit typecasts to accommodate type
mismatches described above.
- Tweak aliasing information so this type mismatching doesn't generate bad
code.
I believe Steven Bosscher had a go at implementing this, but never got it to
work properly.
Paul
[1] A top level Program Unit is a file-level subroutine, function, program or
module. If a PU has contained procedures these procedures and the parent
procedure constitute a single PU.