在C ++中,通常将可能内联的小函数放入头文件中,以便在不依赖链接时优化或其他魔法的情况下进行内联。最常见的是,对于类的访问器方法(在operator[]
上考虑std::vector
)。我现在在现代Fortran中遇到某种类似的行为。
假设我有一个模块,它定义了带有一些私有数据的派生类型,并带有一个简单的访问器:
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
现在我编写一个程序来使用访问器:
program testtype
use FooMod
type(FooType) :: foo
integer :: val
call foo%init(10)
val = foo%get(2)
write(*,*)val
endprogram
使用-O3
编译gfortran 5.4.0:
gfortran -c -O3 foo.f90
gfortran -O3 -S testfoo.f90 foo.o
产生如下输出:
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
所以仍然有一个没有内联的电话。我知道,因为get()
例程的定义在不同的翻译单元中,与C ++标题的文本包含相比,内联可能有点困难,但我认为这是{{{{1}的目的的一部分。 1}}由编译器生成的文件。
有没有办法让像这样的小功能内联模块?如果没有,那么在良好的数据封装现代编程实践的背景下,这似乎是语言/实现的严重缺陷。
我尝试了.mod
标志,看看这是否会有所帮助,但这只是将GIMPLE作为ascii文本字段吐出到"程序集"输出,所以很难说出它在做什么。
谢谢!
一些澄清:我在C ++中意识到-flto
,它的真正含义,它有点用词不当,并且非常熟悉内联的概念;它是如何工作的,需要满足什么条件,没有标题时为什么它很难,以及LTO适合图片的地方。我主要的沮丧是因为Fortran有正式的模块,允许编译器生成特定于实现的.mod文件,为什么这么难?为什么我们需要明确地使用与语言无关的大枪,链接时(困难)内联来执行像内联类型绑定访问器函数这样简单的操作。编译器是否可以存储GIMPLE代码,甚至是.mod文件中的函数文本并进行编译时内联?也许我有一些微妙的缺失。
答案 0 :(得分:2)
在C / C ++中,您通常会#include包含访问者函数定义的标头。因此,在预处理之后,定义与调用站点位于相同的C / C ++转换单元中,即使是编译器的前端,也可以内联函数。
在Fortran中,PROGRAM,MODULE,外部SUBROUTINE,外部FUNCTION和BLOCK DATA都定义了单独的编译单元。即使它们出现在同一个源文件中,编译器也可以将它们视为出现在单独的文件中。有些编译器会这样做,因此无法在没有链路时间优化(LTO)的情况下进行内联。如果definitoins出现在与呼叫站点相同的源文件中,其他人会尝试内联。我知道只有一个编译器将模块过程的定义嵌入到.mod文件中,以便它们可以在任何模块使用的地方内联。
TL; DR如果您希望在Fortran中内联这些功能,则必须启用LTO。