Подтвердить что ты не робот

Каковы эти, казалось бы, бесполезные инструкции callq в объектных файлах x86?

У меня есть тяжелый код С++, который я хочу обеспечить, чтобы компилятор максимально оптимизировался из-за большого объема информации, которую он имел во время компиляции. Чтобы оценить его производительность, я решил взглянуть на разбор объектного файла, который он создает. Ниже приведен фрагмент того, что я получил от 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, часто встречаются во всем сгенерированном коде, и из того, что я могу сказать, являются не чем иным, как no-ops; все они, похоже, просто попадают в следующую инструкцию. Поэтому возникает вопрос, есть ли причина, почему все эти инструкции генерируются?

Я обеспокоен размером кэша инструкций сгенерированного кода, поэтому тратить 5 байт много раз на всю функцию, кажется контрпродуктивным. Кажется немного тяжеловесом для nop, если компилятор не пытается сохранить какое-то выравнивание памяти или что-то в этом роде. Я бы не удивился, если бы это было так.

Я скомпилировал свой код, используя g++ 4.8.5, используя -O3 -fomit-frame-pointer. Для чего это стоит, я видел подобное генерирование кода, используя clang 3.7.

4b9b3361

Ответ 1

00 00 00 00 (относительный) адрес цели в e8 00 00 00 00 предназначен для заполнения компоновщиком. Это не означает, что вызов проваливается. Это просто означает, что вы разбираете объектный файл, который еще не был связан.

Кроме того, вызов следующей команды, если это был конечный результат после фазы ссылки, не будет no-op, потому что он меняет стек (определенный намек на то, что это не то, что происходит в вашем дело).