【问题标题】:What are these seemingly-useless callq instructions in my x86 object files for?我的 x86 目标文件中这些看似无用的 callq 指令有什么用?
【发布时间】:2016-04-20 00:10:13
【问题描述】:

我有一些模板繁重的 C++ 代码,我想确保编译器尽可能优化,因为它在编译时拥有大量信息。为了评估它的性能,我决定看看它生成的目标文件的反汇编。以下是我从objdump -dC 得到的信息:

0000000000000000 <bar<foo, 0u>::get(bool)>:
   0:   41 57                   push   %r15
   2:   49 89 f7                mov    %rsi,%r15
   5:   41 56                   push   %r14
   7:   41 55                   push   %r13
   9:   41 54                   push   %r12
   b:   55                      push   %rbp
   c:   53                      push   %rbx
   d:   48 81 ec 68 02 00 00    sub    $0x268,%rsp
  14:   48 89 7c 24 10          mov    %rdi,0x10(%rsp)
  19:   48 89 f7                mov    %rsi,%rdi
  1c:   89 54 24 1c             mov    %edx,0x1c(%rsp)
  20:   e8 00 00 00 00          callq  25 <bar<foo, 0u>::get(bool)+0x25>
  25:   84 c0                   test   %al,%al
  27:   0f 85 eb 00 00 00       jne    118 <bar<foo, 0u>::get(bool)+0x118>
  2d:   48 c7 44 24 08 00 00    movq   $0x0,0x8(%rsp)
  34:   00 00 
  36:   4c 89 ff                mov    %r15,%rdi
  39:   4d 8d b7 30 01 00 00    lea    0x130(%r15),%r14
  40:   e8 00 00 00 00          callq  45 <bar<foo, 0u>::get(bool)+0x45>
  45:   84 c0                   test   %al,%al
  47:   88 44 24 1b             mov    %al,0x1b(%rsp)
  4b:   0f 85 ef 00 00 00       jne    140 <bar<foo, 0u>::get(bool)+0x140>
  51:   80 7c 24 1c 00          cmpb   $0x0,0x1c(%rsp)
  56:   0f 85 24 03 00 00       jne    380 <bar<foo, 0u>::get(bool)+0x380>
  5c:   48 8b 44 24 10          mov    0x10(%rsp),%rax
  61:   c6 00 00                movb   $0x0,(%rax)
  64:   80 7c 24 1b 00          cmpb   $0x0,0x1b(%rsp)
  69:   75 25                   jne    90 <bar<foo, 0u>::get(bool)+0x90>
  6b:   48 8b 74 24 10          mov    0x10(%rsp),%rsi
  70:   4c 89 ff                mov    %r15,%rdi
  73:   e8 00 00 00 00          callq  78 <bar<foo, 0u>::get(bool)+0x78>
  78:   48 8b 44 24 10          mov    0x10(%rsp),%rax
  7d:   48 81 c4 68 02 00 00    add    $0x268,%rsp
  84:   5b                      pop    %rbx
  85:   5d                      pop    %rbp
  86:   41 5c                   pop    %r12
  88:   41 5d                   pop    %r13
  8a:   41 5e                   pop    %r14
  8c:   41 5f                   pop    %r15
  8e:   c3                      retq   
  8f:   90                      nop
  90:   4c 89 f7                mov    %r14,%rdi
  93:   e8 00 00 00 00          callq  98 <bar<foo, 0u>::get(bool)+0x98>
  98:   83 f8 04                cmp    $0x4,%eax
  9b:   74 f3                   je     90 <bar<foo, 0u>::get(bool)+0x90>
  9d:   85 c0                   test   %eax,%eax
  9f:   0f 85 e4 08 00 00       jne    989 <bar<foo, 0u>::get(bool)+0x989>
  a5:   49 83 87 b0 01 00 00    addq   $0x1,0x1b0(%r15)
  ac:   01 
  ad:   49 8d 9f 58 01 00 00    lea    0x158(%r15),%rbx
  b4:   48 89 df                mov    %rbx,%rdi
  b7:   e8 00 00 00 00          callq  bc <bar<foo, 0u>::get(bool)+0xbc>
  bc:   49 8d bf 80 01 00 00    lea    0x180(%r15),%rdi
  c3:   e8 00 00 00 00          callq  c8 <bar<foo, 0u>::get(bool)+0xc8>
  c8:   48 89 df                mov    %rbx,%rdi
  cb:   e8 00 00 00 00          callq  d0 <bar<foo, 0u>::get(bool)+0xd0>
  d0:   4c 89 f7                mov    %r14,%rdi
  d3:   e8 00 00 00 00          callq  d8 <bar<foo, 0u>::get(bool)+0xd8>
  d8:   83 f8 04                cmp    $0x4,%eax

这个特定函数的反汇编仍在继续,但我注意到的一件事是相对大量的call这样的指令:

20:   e8 00 00 00 00          callq  25 <bar<foo, 0u>::get(bool)+0x25>

这些指令,总是带有操作码e8 00 00 00 00,在生成的代码中经常出现,据我所知,无非是无操作;他们似乎都只是通过下一个指令。那么,这引出了一个问题,是否有充分的理由生成所有这些指令?

我担心生成代码的指令缓存占用空间,因此在一个函数中多次浪费 5 个字节似乎会适得其反。 nop 似乎有点重量级,除非编译器试图保留某种内存对齐或其他东西。如果是这样的话,我不会感到惊讶。

我使用 -O3 -fomit-frame-pointer 使用 g++ 4.8.5 编译了我的代码。值得一提的是,我看到使用 clang 3.7 生成类似的代码。

【问题讨论】:

  • @PascalCuoq:谢谢;我想这可能应该是显而易见的。如果您想转换为答案,这对我来说已经足够好了。
  • 好的,已转换并稍作编辑。

标签: c++ assembly x86 compiler-optimization


【解决方案1】:

e8 00 00 00 00 中的00 00 00 00(相对)目标地址旨在由链接器填写。这并不意味着通话失败。这只是意味着您正在反汇编尚未链接的目标文件。

另外,对下一条指令的调用,如果这是链接阶段之后的最终结果,则不会是空操作,因为它会更改堆栈(一定暗示这不是你的案子)。

【讨论】:

  • @PascalCuoq:你完全正确。当我反汇编目标文件链接到的共享库时,我得到了更清晰的结果。所有callqs 对应于函数内的相对条件跳转。
  • 仅供参考,您可以通过将 -r 传递给 objdump 来检查重定位。一个例子:objdump -dCr 给出类似:``` 4: e8 00 00 00 00 callq 9 5: R_X86_64_PLT32 f()-0x4 ```
  • 我一般用alias disas='objdump -drwC -Mintel'
猜你喜欢
  • 2018-03-26
  • 2012-10-05
  • 1970-01-01
  • 2015-10-15
  • 2018-08-25
相关资源
最近更新 更多