链接器可以内联函数吗?

Posted

技术标签:

【中文标题】链接器可以内联函数吗?【英文标题】:Can the linker inline functions? 【发布时间】:2011-08-24 15:03:30 【问题描述】:

在文件file1.c 中,调用了在文件file2.c 中实现的函数。 当我将file1.ofile2.o链接到一个可执行文件中时,如果file2中的函数很小,链接器会自动检测到函数很小并内联它的调用吗?

【问题讨论】:

一些链接器可以,是的(Visual C++ 链接器有一个称为“链接时间代码生成”的功能,它执行跨模块内联和优化)。您使用的链接器是否可以或将要这样做,这是不可能的,因为您还没有告诉我们您使用的是什么链接器(即使那样,唯一确定的真正方法是找出什么代码您的链接器生成...)。 【参考方案1】:

除了 Jame McNellis 提到的对链接时间代码生成 (LTCG) 的支持之外,GCC 工具链还支持链接时间优化。从 4.5 版开始,GCC 支持-flto 开关,该开关启用链接时间优化 (LTO),这是一种整体程序优化形式,允许它从单独的目标文件内联函数(以及编译器可能进行的任何其他优化,如果它正在编译所有目标文件,就好像它们来自单个 C 源文件一样)。

这是一个简单的例子:

test.c

void print_int(int x);

int main()
    print_int(1);
    print_int(42);
    print_int(-1);

    return 0;

print_int.c

#include <stdio.h>

void print_int( int x)

    printf( "the int is %d\n", x);

首先使用 GCC4.5.x 编译它们 - 来自 GCC 文档的示例使用 -O2,但为了在我的简单测试中获得可见的结果,我不得不使用 -O3

C:\temp>gcc --version
gcc (GCC) 4.5.2

# compile with preparation for LTO
C:\temp>gcc -c -O3 -flto test.c
C:\temp>gcc -c -O3 -flto print_int.c

# link without LTO
C:\temp>gcc -o test-nolto.exe  print_int.o test.o

要获得 LTO 的效果,您甚至应该在链接阶段使用优化选项 - 链接器实际上调用编译器来编译编译器在上述第一步中放入目标文件的中间代码片段。如果您在此阶段也没有通过优化选项,编译器将不会执行您要查找的内联。

# link using LTO
C:\temp>gcc -o test-lto.exe -flto -O3 print_int.o test.o

没有链接时间优化的版本的反汇编。请注意,调用是对print_int() 函数进行的:

C:\temp>gdb test-nolto.exe
GNU gdb (GDB) 7.2
(gdb) start
Temporary breakpoint 1 at 0x401373
Starting program: C:\temp/test-nolto.exe
[New Thread 3324.0xdc0]

Temporary breakpoint 1, 0x00401373 in main ()
(gdb) disassem
Dump of assembler code for function main:
   0x00401370 <+0>:     push   %ebp
   0x00401371 <+1>:     mov    %esp,%ebp
=> 0x00401373 <+3>:     and    $0xfffffff0,%esp
   0x00401376 <+6>:     sub    $0x10,%esp
   0x00401379 <+9>:     call   0x4018ca <__main>
   0x0040137e <+14>:    movl   $0x1,(%esp)
   0x00401385 <+21>:    call   0x401350 <print_int>
   0x0040138a <+26>:    movl   $0x2a,(%esp)
   0x00401391 <+33>:    call   0x401350 <print_int>
   0x00401396 <+38>:    movl   $0xffffffff,(%esp)
   0x0040139d <+45>:    call   0x401350 <print_int>
   0x004013a2 <+50>:    xor    %eax,%eax
   0x004013a4 <+52>:    leave
   0x004013a5 <+53>:    ret

具有链接时间优化的版本的反汇编。请注意,直接调用printf()

C:\temp>gdb test-lto.exe

GNU gdb (GDB) 7.2
(gdb) start
Temporary breakpoint 1 at 0x401373
Starting program: C:\temp/test-lto.exe
[New Thread 1768.0x126c]

Temporary breakpoint 1, 0x00401373 in main ()
(gdb) disassem
Dump of assembler code for function main:
   0x00401370 <+0>:     push   %ebp
   0x00401371 <+1>:     mov    %esp,%ebp
=> 0x00401373 <+3>:     and    $0xfffffff0,%esp
   0x00401376 <+6>:     sub    $0x10,%esp
   0x00401379 <+9>:     call   0x4018da <__main>
   0x0040137e <+14>:    movl   $0x1,0x4(%esp)
   0x00401386 <+22>:    movl   $0x403064,(%esp)
   0x0040138d <+29>:    call   0x401acc <printf>
   0x00401392 <+34>:    movl   $0x2a,0x4(%esp)
   0x0040139a <+42>:    movl   $0x403064,(%esp)
   0x004013a1 <+49>:    call   0x401acc <printf>
   0x004013a6 <+54>:    movl   $0xffffffff,0x4(%esp)
   0x004013ae <+62>:    movl   $0x403064,(%esp)
   0x004013b5 <+69>:    call   0x401acc <printf>
   0x004013ba <+74>:    xor    %eax,%eax
   0x004013bc <+76>:    leave
   0x004013bd <+77>:    ret
End of assembler dump.

这是使用 MSVC 进行的相同实验(首先使用 LTCG):

C:\temp>cl -c /GL /Zi /Ox test.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

test.c

C:\temp>cl -c /GL /Zi /Ox print_int.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

print_int.c

C:\temp>link /LTCG test.obj print_int.obj /out:test-ltcg.exe /debug
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

Generating code
Finished generating code

C:\temp>"\Program Files (x86)\Debugging Tools for Windows (x86)"\cdb test-ltcg.exe

Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: test-ltcg.exe
    // ...
0:000> u main
*** WARNING: Unable to verify checksum for test-ltcg.exe
test_ltcg!main:
00cd1c20 6a01            push    1
00cd1c22 68d05dcd00      push    offset test_ltcg!__decimal_point_length+0x10 (00cd5dd0)
00cd1c27 e8e3f3feff      call    test_ltcg!printf (00cc100f)
00cd1c2c 6a2a            push    2Ah
00cd1c2e 68d05dcd00      push    offset test_ltcg!__decimal_point_length+0x10 (00cd5dd0)
00cd1c33 e8d7f3feff      call    test_ltcg!printf (00cc100f)
00cd1c38 6aff            push    0FFFFFFFFh
00cd1c3a 68d05dcd00      push    offset test_ltcg!__decimal_point_length+0x10 (00cd5dd0)
00cd1c3f e8cbf3feff      call    test_ltcg!printf (00cc100f)
00cd1c44 83c418          add     esp,18h
00cd1c47 33c0            xor     eax,eax
00cd1c49 c3              ret
0:000>

现在没有 LTCG。请注意,使用 MSVC,您必须在没有 /GL 的情况下编译 .c 文件,以防止链接器执行 LTCG - 否则链接器检测到指定了 /GL,它将强制使用 /LTCG 选项(嘿,那是你第一次说你想要什么/GL):

C:\temp>cl -c /Zi /Ox test.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

test.c

C:\temp>cl -c /Zi /Ox print_int.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

print_int.c

C:\temp>link test.obj print_int.obj /out:test-noltcg.exe /debug
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

C:\temp>"\Program Files (x86)\Debugging Tools for Windows (x86)"\cdb test-noltcg.exe

Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: test-noltcg.exe
// ...
0:000> u main
test_noltcg!main:
00c41020 6a01            push    1
00c41022 e8e3ffffff      call    test_noltcg!ILT+5(_print_int) (00c4100a)
00c41027 6a2a            push    2Ah
00c41029 e8dcffffff      call    test_noltcg!ILT+5(_print_int) (00c4100a)
00c4102e 6aff            push    0FFFFFFFFh
00c41030 e8d5ffffff      call    test_noltcg!ILT+5(_print_int) (00c4100a)
00c41035 83c40c          add     esp,0Ch
00c41038 33c0            xor     eax,eax
00c4103a c3              ret
0:000>

Microsoft 的链接器在 LTCG 中支持但 GCC 不支持的一件事(据我所知) 是 Profile Guided Optimization (PGO)。该技术允许微软的链接器基于从程序之前运行收集的分析数据进行优化。这允许链接器做一些事情,例如将“热”函数收集到相同的内存页面上,并且很少使用代码序列到其他内存页面上,以减少程序的工作集。

 


编辑(2011 年 8 月 28 日):GCC 支持使用 -fprofile-generate-fprofile-use 等选项进行配置文件引导优化,但我完全不了解它们。

感谢 Konrad Rudolph 向我指出这一点。

【讨论】:

GCC 还通过-fprofile-generate-fprofile-use 支持PGO。 @Konrad:哇——我完全不知道。我得调查一下。谢谢! 但是 LTO 不是由链接器处理的 (binutils/ld);它是一个编译器(gcc/gcc)。 @osgx:你是对的,但它实际上是由collect2 实现的,它是一种预链接器或链接器包装器 (gcc.gnu.org/onlinedocs/gccint/Collect2.html)。另见gcc.gnu.org/wiki/LinkTimeOptimization

以上是关于链接器可以内联函数吗?的主要内容,如果未能解决你的问题,请参考以下文章

何时可以/将在 C++ 中内联函数?可以强制内联行为吗?

C++基础语法梳理:inline 内联函数!虚函数可以是内联函数吗?

.c 文件中的 C99 内联函数

内联函数真的可以提高程序执行效率吗

OpenCL 内联函数可以返回 OpenCL 类型吗?

内联函数