O inline do linker pode funcionar?

No arquivo file1.c , há uma chamada para uma function que é implementada no arquivo file2.c . Quando eu vinculo file1.o e file2.o em um executável, se a function em file2 é muito pequena, o vinculador detectará automaticamente que a function é pequena e inline é sua chamada?

Além do suporte para Link Time Code Generation (LTCG) que Jame McNellis mencionou, o toolchain do GCC também suporta otimização de tempo de link. A partir da versão 4.5, o GCC suporta a opção -flto que permite a otimização de tempo de link (LTO), uma forma de otimização de programa completa que permite funções in-line a partir de arquivos de object separados (e quaisquer outras otimizações que um compilador poderia fazer se fosse compilar todos os arquivos object como se fossem de um único arquivo fonte C).

Aqui está um exemplo simples:

test.c :

 void print_int(int x); int main(){ print_int(1); print_int(42); print_int(-1); return 0; } 

print_int.c :

 #include  void print_int( int x) { printf( "the int is %d\n", x); } 

Primeiro, compile-os usando o GCC4.5.x – exemplos de documentos do GCC usam o -O2 , mas para obter resultados visíveis no meu teste simples, eu tive que usar o -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 

Para obter o efeito de LTO, você deve usar as opções de otimização até mesmo no estágio de link – o linker realmente invoca o compilador para compilar partes do código intermediário que o compilador colocou no arquivo de object nos primeiros passos acima. Se você não passar a opção de otimização também neste estágio, o compilador não executará o inlining que você estaria procurando.

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

Desassembly da versão sem otimização do tempo de link. Note que as chamadas são feitas para a function 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  0x0040138a < +26>: movl $0x2a,(%esp) 0x00401391 < +33>: call 0x401350  0x00401396 < +38>: movl $0xffffffff,(%esp) 0x0040139d < +45>: call 0x401350  0x004013a2 < +50>: xor %eax,%eax 0x004013a4 < +52>: leave 0x004013a5 < +53>: ret 

Desassembly da versão com otimização de tempo de link. Note que as chamadas para printf() são feitas diretamente:

 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  0x00401392 < +34>: movl $0x2a,0x4(%esp) 0x0040139a < +42>: movl $0x403064,(%esp) 0x004013a1 < +49>: call 0x401acc  0x004013a6 < +54>: movl $0xffffffff,0x4(%esp) 0x004013ae < +62>: movl $0x403064,(%esp) 0x004013b5 < +69>: call 0x401acc  0x004013ba < +74>: xor %eax,%eax 0x004013bc < +76>: leave 0x004013bd < +77>: ret End of assembler dump. 

E aqui está o mesmo experimento com MSVC (primeiro com 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> 

Agora sem LTCG. Note que com o MSVC você tem que compilar o arquivo .c sem o /GL para evitar que o vinculador execute o LTCG – caso contrário, o vinculador detecta que /GL foi especificado e forçará a opção /LTCG (ei, foi o que você disse você queria da primeira vez com /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> 

Uma coisa que o linker da Microsoft suporta no LTCG que não é suportado pelo GCC (tanto quanto eu sei) é o Profile Guided Optimization (PGO). Essa tecnologia permite que o vinculador da Microsoft otimize com base em dados de criação de perfil reunidos em execuções anteriores do programa. Isso permite que o vinculador faça coisas como reunir funções ‘quentes’ nas mesmas páginas de memory e raramente use sequências de código em outras páginas de memory para reduzir o conjunto de trabalho de um programa.


Edit (28 Aug 2011): Otimização guiada do perfil de suporte do GCC usando opções como -fprofile-generate e -fprofile-use , mas estou completamente desinformado sobre elas.

Obrigado a Konrad Rudolph por apontar isso para mim.