Путаница в указателе аннотации в Linux perf_events - PullRequest
0 голосов
/ 28 декабря 2018

Я запустил sudo perf record -F 99 find /, затем sudo perf report и выбрал «Annotate fdopendir», и вот первые семь инструкций:

push %rbp push %rbx mov %edi,%esi mov %edi,%ebx mov $0x1,%edi sub $0xa8,%rsp mov %rsp,%rbp

Первая инструкция, по-видимому, сохраняетуказатель основного кадра вызывающего абонента.Я считаю, что инструкции со 2 по 5 не имеют отношения к этому вопросу, но здесь для полноты.Инструкции 6 и 7 сбивают меня с толку.Не должно ли присвоение rbp rsp до вычитания 0xa8 из rsp?

1 Ответ

0 голосов
/ 30 декабря 2018

x86-64 System V ABI не требует создания традиционного / устаревшего стекового фрейма.Это выглядит близко к традиционной установке фрейма стека, но это определенно не потому, что сразу после первого push %rbp.

. *1005*

мы видим сгенерированный компиляторомкод, который просто использует RBP в качестве временного регистра и использует его для хранения указателя на локальный элемент в стеке. Это просто совпадение, что это случается с использованием инструкции mov %rsp, %rbp через некоторое время после push %rbp. Это не создание стекового фрейма.

В x86-64 System V, RBX и RBP являются единственными 2 «низкими 8» регистрами, которые сохраняются при вызове и, следовательно, могут использоваться без REX.префиксы в некоторых случаях (например, для push / pop и при использовании в режимах адресации), сохраняя размер кода.GCC предпочитает использовать их перед сохранением / восстановлением любого из R12..R15. Какие регистры сохраняются при вызове функции linux x86-64 (Для указателей при копировании их с mov всегда требуется префикс REX для 64-битного размера операнда, поэтому экономия меньше, чем для 32-битовые целые числа, но gcc по-прежнему относится к RBX, а не к RBP, в том порядке, в котором необходимо сохранить / восстановить сохраненные вызовом регистры в функции.)

Разборка /lib/libc.so.6 (glibc) в моей системе (Arch Linux) показывает похожий, но разный код для fdopendir.Вы остановили разборку слишком рано, прежде чем она вызовет функцию.Это проливает некоторый свет на то, почему ему нужен временный регистр, сохраняемый вызовом: ему нужна переменная в регистре для всего вызова.

00000000000c1260 <fdopendir>:
   c1260:       55                      push   %rbp
   c1261:       89 fe                   mov    %edi,%esi
   c1263:       53                      push   %rbx
   c1264:       89 fb                   mov    %edi,%ebx
   c1266:       bf 01 00 00 00          mov    $0x1,%edi
   c126b:       48 81 ec a8 00 00 00    sub    $0xa8,%rsp
   c1272:       64 48 8b 04 25 28 00 00 00      mov    %fs:0x28,%rax    # stack-check cookie
   c127b:       48 89 84 24 98 00 00 00         mov    %rax,0x98(%rsp)
   c1283:       31 c0                   xor    %eax,%eax
   c1285:       48 89 e5                mov    %rsp,%rbp      # save a pointer
   c1288:       48 89 ea                mov    %rbp,%rdx      # and pass it as a function arg
   c128b:       e8 90 7d 02 00          callq  e9020 <__fxstat>
   c1290:       85 c0                   test   %eax,%eax
   c1292:       78 6a                   js     c12fe <fdopendir+0x9e>
   c1294:       8b 44 24 18             mov    0x18(%rsp),%eax
   c1298:       25 00 f0 00 00          and    $0xf000,%eax
   c129d:       3d 00 40 00 00          cmp    $0x4000,%eax
   c12a2:       75 4c                   jne    c12f0 <fdopendir+0x90>
   ....

   c12c1:       48 89 e9                mov    %rbp,%rcx      # pass the pointer as the 4th arg
   c12c4:       89 c2                   mov    %eax,%edx
   c12c6:       31 f6                   xor    %esi,%esi
   c12c8:       89 df                   mov    %ebx,%edi
   c12ca:       e8 d1 f7 ff ff          callq  c0aa0 <__alloc_dir>
   c12cf:       48 8b 8c 24 98 00 00 00         mov    0x98(%rsp),%rcx
   c12d7:       64 48 33 0c 25 28 00 00 00      xor    %fs:0x28,%rcx     # check the stack cookie
   c12e0:       75 38                   jne    c131a <fdopendir+0xba>
   c12e2:       48 81 c4 a8 00 00 00    add    $0xa8,%rsp
   c12e9:       5b                      pop    %rbx
   c12ea:       5d                      pop    %rbp
   c12eb:       c3                      retq   

Это довольно глупый код-ген;gcc мог просто использовать mov %rsp, %rcx во второй раз, когда это было нужно.Я бы назвал это пропущенной оптимизацией.Он никогда не нуждался в этом указателе в регистре с сохранением вызова, потому что он всегда знал, где он был относительно RSP.

(Даже если бы он не был точно в RSP + 0, lea something(%rsp), %rdx и lea something(%rsp), %rcx будутбыли в полном порядке два раза, когда это было необходимо, с, вероятно, меньшей общей стоимостью, чем сохранение / восстановление RBP + требуемые mov инструкции.)

Или он мог бы использовать mov 0x18(%rbp),%eax вместо rsp для сохранениябайт размера кода в этом режиме адресации.Избегание прямых ссылок на RSP между вызовами функций уменьшает количество операций синхронизации стека, которые необходимо вставить процессорам Intel.

...