ARM: Зачем мне нужно вызывать/записывать два регистра при вызовах функций?

Я понимаю, что мне нужно нажать Link Register в начале вызова функции и поместить это значение в Program Couter перед возвратом, так что выполнение может нести одно из того, где оно было до вызова функции.

Я не понимаю, почему большинство людей делают это, добавляя дополнительный регистр в push/pop. Например:

push {ip, lr}
...
pop {ip, pc}

Например, здесь Hello World в ARM, предоставленном официальным блоком ARM

Вопрос 1: какая причина для "фиктивного регистра", как они его называют? Почему бы просто не нажать {lr} и pop {pc}? Говорят, что он поддерживает выравнивание в 8 байт, но не соответствует ли 4-байтовый стек?

Вопрос 2: какой регистр является "ip" (т.е. r7 или что?)

Ответ 1

какая причина для "фиктивного регистра", как они его называют? Почему бы просто не нажать {lr} и pop {pc}? Говорят, что он поддерживает выравнивание в 8 байт, но не соответствует ли 4-байтовый стек?

Стек требует только 4-байтового выравнивания; но, если шина данных имеет ширину 64 бита (как на многих современных ARM), более эффективно поддерживать ее при 8-байтовом выравнивании. Затем, например, если вы вызываете функцию, которая должна складывать два регистра, это можно сделать в одной 64-разрядной записи вместо двух 32-разрядных записей.

ОБНОВЛЕНИЕ: По-видимому, это не только для эффективности; это требование официального стандартного вызова процедуры, как указано в комментариях.

Если вы нацеливаете старые 32-битные ARM, то дополнительный стековый регистр может немного ухудшить производительность.

какой регистр является "ip" (т.е. r7 или что?)

r12. См., Например, здесь для полного набора псевдонимов регистра, используемых стандартом вызова процедуры.

Ответ 2

8-байтовое выравнивание является требованием для взаимодействия между объектами, соответствующими AAPCS.

У ARM есть консультативная записка по этому вопросу:

ABI для ARM® Architecture Advisory Note - SP должен быть 8-байтовым, выровненным при входе в AAPCS-совместимые функции

В статье упоминаются две причины использования 8-байтового выравнивания

  • Ошибка выравнивания или непобедимое поведение. (Связанные с оборудованием/архитектурой причины - LDRD/STRD может вызвать ошибку выравнивания или показать поведение НЕПРЕРЫВНОСТИ на архитектурах, отличных от ARMv7)

  • Ошибка приложения. (Компилятор - различия в допуске Runtime, они дают в качестве примера va_start и va_arg)

Конечно, это все об открытых интерфейсах, если вы делаете статический исполняемый файл без дополнительной привязки, вы можете выровнять стек с 4 байтами.

Ответ 3

Поскольку вы хотите сохранить и восстановить их после выполнения своей функции. На входе функции он сохраняет регистры ip и lr (с именем prolog). После завершения функции он назначает оба (epilog):

pc <- lr

ip <- old_ip

ИЗМЕНИТЬ

Регистр r12 также упоминается как ip и используется как регистр ошибок внутри процедуры процедуры, см. .

Согласие заключается в том, что функция вызываемого абонента может изменить ip,r0-r3, поэтому вы должны восстановить их, зависит от соглашения о вызовах

EDIT2: Почему мы можем захотеть, чтобы стек был выровнен по 8 на ARM.

Если стек не выравнивается по восемью байтам, использование LDRD и STRD (загрузка и сохранение двойного слова) может вызывают ошибку выравнивания, в зависимости от цели и конфигурации б.

Заметьте что у нас такая же проблема на X86, и на Mac OS у нас есть выравнивание по 16 байт