Reputation: 848
In C++ it is common to put small functions that are likely to be inlined into a header file to make inlining possible without resorting to link-time optimization or other sorcery. Most commonly, for accessor methods on classes (think operator[]
on std::vector
). I'm having a heck of a time getting some sort of similar behavior in modern Fortran.
Say I have a module that defines a derived type with some private data, with a simple accessor like this:
module FooMod
type :: FooType
integer,allocatable,private :: d(:)
contains
procedure,pass :: init
procedure,pass :: get
procedure,pass :: clear
endtype
contains
subroutine init(this,n)
class(FooType),intent(inout) :: this
integer,intent(in) :: n
integer :: i
allocate(this%d(n))
do i=1,n
this%d(i)=i
enddo
endsubroutine
function get(this,i) result(val)
class(FooType),intent(in) :: this
integer,intent(in) :: i
integer :: val
val = this%d(i)
endfunction
subroutine clear(this)
class(FooType),intent(inout) :: this
deallocate(this%d)
endsubroutine
endmodule
Now I write a program to use the accessor:
program testtype
use FooMod
type(FooType) :: foo
integer :: val
call foo%init(10)
val = foo%get(2)
write(*,*)val
endprogram
Compiling with gfortran 5.4.0 with -O3
:
gfortran -c -O3 foo.f90
gfortran -O3 -S testfoo.f90 foo.o
produces output like this:
call __foomod_MOD_get
leaq 16(%rsp), %rdi
movl %eax, 12(%rsp)
movq $.LC2, 24(%rsp)
movl $11, 32(%rsp)
movl $128, 16(%rsp)
movl $6, 20(%rsp)
call _gfortran_st_write
So there is still a call happening with no inlining. I get that since the definition of the get()
routine is in a different translation unit, inlining it is maybe a little difficult compared to the textual inclusion of C++ headers, but I thought that was part of the purpose of the .mod
files that get generated by the compiler.
Is there any way to get little functions like this to inline across modules? If there isn't, this seems like a serious deficiency in the language/implementation in the context of good modern programming practice of data encapsulation.
I tried the -flto
flag to see if that would help, but that just spits GIMPLE as ascii text fields to the "assembly" output, so it's hard to tell what it's doing.
Thanks!
Some clarifications: I'm aware of inline
in C++, its true meaning and that it's somewhat of a misnomer, and pretty familiar with the concept of inlining; how it works, what conditions need to be satisfied, why it's hard without headers, and where LTO fits into the picture. My main frustration is that since Fortran has formal modules, for which compiler is allowed to generate implementation-specific .mod files, why is it so difficult? Why do we need to explicitly resort to the big guns of language-agnostic, link-time (difficult) inlining to do something as simple as inlining type-bound accessor functions. Can't the compiler store GIMPLE code, or even the function text in the .mod file and do compile-time inlining? Maybe there is some subtlety that I'm missing.
Upvotes: 2
Views: 484
Reputation: 361
In C/C++, you typically #include a header containing the accessor function definitions. So after preprocessing, the definitions are in the same C/C++ translation unit as the call site and the function can be inlined, even by the compiler's frontend.
In Fortran, PROGRAM, MODULE, external SUBROUTINE, external FUNCTION, and BLOCK DATA all define separate compilation units. Even if they appear in the same source file, the compiler can treat them as if they appeared in separate files. Some compilers do, and therefore can't inline without Link Time Optimization (LTO). Others try to inline if the definitoins appear in the same source file as the call sites. I know of only one compiler that embeds the definitions of module procedures in their .mod files so that they can inline anywhere the module is used.
TL;DR You have to enable LTO if you want these functions inlined in Fortran.
Upvotes: 3