派生类型访问器函数的跨模块内联

时间:2017-07-26 15:33:07

标签: fortran gfortran

在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文件中的函数文本并进行编译时内联?也许我有一些微妙的缺失。

1 个答案:

答案 0 :(得分:2)

在C / C ++中,您通常会#include包含访问者函数定义的标头。因此,在预处理之后,定义与调用站点位于相同的C / C ++转换单元中,即使是编译器的前端,也可以内联函数。

在Fortran中,PROGRAM,MODULE,外部SUBROUTINE,外部FUNCTION和BLOCK DATA都定义了单独的编译单元。即使它们出现在同一个源文件中,编译器也可以将它们视为出现在单独的文件中。有些编译器会这样做,因此无法在没有链路时间优化(LTO)的情况下进行内联。如果definitoins出现在与呼叫站点相同的源文件中,其他人会尝试内联。我知道只有一个编译器将模块过程的定义嵌入到.mod文件中,以便它们可以在任何模块使用的地方内联。

TL; DR如果您希望在Fortran中内联这些功能,则必须启用LTO。