用gcc理解内联(内联函数汇编代码存在于二进制文件中?)

时间:2018-05-18 09:13:25

标签: c gcc assembly inline

我试过这段代码:

#include <stdlib.h>
#include <stdio.h>

__attribute__((always_inline)) void dummy_fct(int *a){
  float b[2] = {(float)a[0] / 42,(float)a[1] / 66};
  a[0] = (int)b[0];
  a[1] = (int)b[1];
}

int main(void){
  int v[2] = {rand(),rand()};
  dummy_fct(v);
  printf("%d %d\n",v[0],v[1]);
  return 0;
}

当我用gcc编译它时没有选项我得到这个警告(这是合乎逻辑的):

 main.c:4:37: warning: always_inline function might not be inlinable [-Wattributes]
 __attribute__((always_inline)) void dummy_fct(int *a){

但是当我读到生成的二进制程序集(使用objdump -d my_binary)时,我得到了这个:

(...)
0000000000400550 <frame_dummy>:
  400550:   55                      push   %rbp
  400551:   48 89 e5                mov    %rsp,%rbp
  400554:   5d                      pop    %rbp
  400555:   e9 36 ff ff ff          jmpq   400490 <register_tm_clones>
  40055a:   90                      nop
  40055b:   90                      nop

000000000040055c <dummy_fct>:
  40055c:   55                      push   %rbp
  40055d:   48 89 e5                mov    %rsp,%rbp
  400560:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
  400564:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  400568:   8b 00                   mov    (%rax),%eax
  40056a:   f3 0f 2a c0             cvtsi2ss %eax,%xmm0
  40056e:   f3 0f 10 0d ea 01 00    movss  0x1ea(%rip),%xmm1        # 400760 <_IO_stdin_used+0xc>
  400575:   00 
  400576:   f3 0f 5e c1             divss  %xmm1,%xmm0
  40057a:   f3 0f 11 45 f8          movss  %xmm0,-0x8(%rbp)
  40057f:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  400583:   48 83 c0 04             add    $0x4,%rax
  400587:   8b 00                   mov    (%rax),%eax
  400589:   f3 0f 2a c0             cvtsi2ss %eax,%xmm0
  40058d:   f3 0f 10 0d cf 01 00    movss  0x1cf(%rip),%xmm1        # 400764 <_IO_stdin_used+0x10>
  400594:   00 
  400595:   f3 0f 5e c1             divss  %xmm1,%xmm0
  400599:   f3 0f 11 45 fc          movss  %xmm0,-0x4(%rbp)
  40059e:   f3 0f 10 45 f8          movss  -0x8(%rbp),%xmm0
  4005a3:   f3 0f 2c d0             cvttss2si %xmm0,%edx
  4005a7:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  4005ab:   89 10                   mov    %edx,(%rax)
  4005ad:   f3 0f 10 45 fc          movss  -0x4(%rbp),%xmm0
  4005b2:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  4005b6:   48 8d 50 04             lea    0x4(%rax),%rdx
  4005ba:   f3 0f 2c c0             cvttss2si %xmm0,%eax
  4005be:   89 02                   mov    %eax,(%rdx)
  4005c0:   90                      nop
  4005c1:   5d                      pop    %rbp
  4005c2:   c3                      retq   

00000000004005c3 <main>:
  4005c3:   55                      push   %rbp
  4005c4:   48 89 e5                mov    %rsp,%rbp
  4005c7:   48 83 ec 20             sub    $0x20,%rsp
  4005cb:   e8 28 fe ff ff          callq  4003f8 <rand@plt>
  4005d0:   89 45 f0                mov    %eax,-0x10(%rbp)
  4005d3:   e8 20 fe ff ff          callq  4003f8 <rand@plt>
  4005d8:   89 45 f4                mov    %eax,-0xc(%rbp)
  4005db:   48 8d 45 f0             lea    -0x10(%rbp),%rax
  4005df:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  4005e3:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  4005e7:   8b 00                   mov    (%rax),%eax
  4005e9:   f3 0f 2a c0             cvtsi2ss %eax,%xmm0
  4005ed:   f3 0f 10 0d 6b 01 00    movss  0x16b(%rip),%xmm1        # 400760 <_IO_stdin_used+0xc>
  4005f4:   00 
  4005f5:   f3 0f 5e c1             divss  %xmm1,%xmm0
  4005f9:   f3 0f 11 45 e8          movss  %xmm0,-0x18(%rbp)
  4005fe:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  400602:   48 83 c0 04             add    $0x4,%rax
  400606:   8b 00                   mov    (%rax),%eax
  400608:   f3 0f 2a c0             cvtsi2ss %eax,%xmm0
  40060c:   f3 0f 10 0d 50 01 00    movss  0x150(%rip),%xmm1        # 400764 <_IO_stdin_used+0x10>
  400613:   00 
  400614:   f3 0f 5e c1             divss  %xmm1,%xmm0
  400618:   f3 0f 11 45 ec          movss  %xmm0,-0x14(%rbp)
  40061d:   f3 0f 10 45 e8          movss  -0x18(%rbp),%xmm0
  400622:   f3 0f 2c d0             cvttss2si %xmm0,%edx
  400626:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  40062a:   89 10                   mov    %edx,(%rax)
  40062c:   f3 0f 10 45 ec          movss  -0x14(%rbp),%xmm0
  400631:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  400635:   48 8d 50 04             lea    0x4(%rax),%rdx
  400639:   f3 0f 2c c0             cvttss2si %xmm0,%eax
  40063d:   89 02                   mov    %eax,(%rdx)
  40063f:   8b 55 f4                mov    -0xc(%rbp),%edx
  400642:   8b 45 f0                mov    -0x10(%rbp),%eax
  400645:   89 c6                   mov    %eax,%esi
  400647:   bf 58 07 40 00          mov    $0x400758,%edi
  40064c:   b8 00 00 00 00          mov    $0x0,%eax
  400651:   e8 82 fd ff ff          callq  4003d8 <printf@plt>
  400656:   b8 00 00 00 00          mov    $0x0,%eax
  40065b:   c9                      leaveq 
  40065c:   c3                      retq   
  40065d:   90                      nop
  40065e:   90                      nop
  40065f:   90                      nop
(...)

当然,没有callqdummy_fct但是我很惊讶地看到函数的代码不在main中,并且在二进制文件中存在于一个单独的部分中。

我的意思是内联已经添加了一些代码,因此二进制文件应该更大(如果内联代码被多次使用)。那么为什么函数的二进制仍然存在于main之外呢?所以二进制文件甚至更大...... 关于gcc的内联,我真的不明白这一点。

(这是一个哲学问题,这里没有更多的意义)

2 个答案:

答案 0 :(得分:3)

由于您没有另外声明,dummy_fct是一个外部函数,因此如果存在来自其他翻译单元的调用者,编译器必须为其生成代码。要删除此义务,请将dummy_fct声明为static

进一步注意dummy_fct实际上是内联的。您可以在main

中查看其机器代码
  4005db:   48 8d 45 f0             lea    -0x10(%rbp),%rax
  4005df:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  4005e3:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  4005e7:   8b 00                   mov    (%rax),%eax
  4005e9:   f3 0f 2a c0             cvtsi2ss %eax,%xmm0
  4005ed:   f3 0f 10 0d 6b 01 00    movss  0x16b(%rip),%xmm1        # 400760 <_IO_stdin_used+0xc>
  4005f4:   00 
  4005f5:   f3 0f 5e c1             divss  %xmm1,%xmm0
  4005f9:   f3 0f 11 45 e8          movss  %xmm0,-0x18(%rbp)
  4005fe:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  400602:   48 83 c0 04             add    $0x4,%rax
  400606:   8b 00                   mov    (%rax),%eax
  400608:   f3 0f 2a c0             cvtsi2ss %eax,%xmm0
  40060c:   f3 0f 10 0d 50 01 00    movss  0x150(%rip),%xmm1        # 400764 <_IO_stdin_used+0x10>
  400613:   00 
  400614:   f3 0f 5e c1             divss  %xmm1,%xmm0
  400618:   f3 0f 11 45 ec          movss  %xmm0,-0x14(%rbp)
  40061d:   f3 0f 10 45 e8          movss  -0x18(%rbp),%xmm0
  400622:   f3 0f 2c d0             cvttss2si %xmm0,%edx
  400626:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  40062a:   89 10                   mov    %edx,(%rax)
  40062c:   f3 0f 10 45 ec          movss  -0x14(%rbp),%xmm0
  400631:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  400635:   48 8d 50 04             lea    0x4(%rax),%rdx
  400639:   f3 0f 2c c0             cvttss2si %xmm0,%eax
  40063d:   89 02                   mov    %eax,(%rdx)

虽然由于缺乏优化而可能有点难以阅读此代码,但仔细阅读确认这确实是dummy_fct所做的。

答案 1 :(得分:3)

你忘记了神奇的单词inlineinline __attribute((always inline))!= __attribute((always inline))

即使您添加inline,有时也会看到相同的附加代码。该代码实际上不会被链接,LTO会使用它。

因为有一些混乱。即使您不使用inline关键字

,编译器也可以内联函数

https://godbolt.org/g/J98BgZ

__attribute__((always_inline))

向编译器显示它应该内联,即使它通常不会内联(例如-O0)。

添加C关键字inline最终使函数内联,并使用属性使其始终内联。