Что регистрирует сохранение в соглашении вызова ARM C?

Прошло некоторое время с тех пор, как я последний раз закодировал ассемблер, и я немного ржавчивый по деталям. Если я вызываю функцию C из руки, мне остается только беспокоиться о сохранении r0-r3 и lr, правильно?

Если функция C использует любые другие регистры, отвечает ли она за сохранение в стеке и восстановление их? Другими словами, компилятор будет генерировать код для этого для функций C.

Например, если я использую функцию r10 в функции ассемблера, мне не нужно подталкивать ее значение в стек или в память и вызывать / восстанавливать ее после вызова C, не так ли?

Это для arm-eabi-gcc 4.3.0.

Это зависит от ABI для платформы, для которой вы компилируете. В Linux есть два ARI ABI; старый и новый. AFAIK, новый (EABI) на самом деле является AAPCS ARM. В настоящее время полные определения EABI живут здесь, в информационном центре ARM .

Из AAPCS, §5.1.1 :

  • r0-r3 – регистры аргументов и царапин; r0-r1 также являются результирующими регистрами
  • r4-r8 – регистры сохранения сбережений
  • r9 может быть регистром сохранения или нет (по некоторым вариантам AAPCS это специальный регистр)
  • r10-r11 – регистры с сохранением вызываемого абонента
  • r12-r15 являются специальными регистрами

Регистр регистрации вызываемого абонента должен быть сохранен вызываемым пользователем (в противовес регистру сохранения вызывающего абонента, в котором вызывающий абонент сохраняет регистр); поэтому, если это ABI, который вы используете, вам не нужно сохранять r10 перед вызовом другой функции (другая функция отвечает за ее сохранение).

Изменить: какой компилятор вы используете, не имеет никакого значения; gcc, в частности, может быть настроен для нескольких различных ABI, и его можно даже изменить в командной строке. Если посмотреть на код prologа / эпилога, который он генерирует, это не так полезно, поскольку он предназначен для каждой функции, и компилятор может использовать другие способы сохранения регистра (например, сохранение его в середине функции).

Чтобы добавить недостающую информацию в регистры NEON:

Из AAPCS , §5.1.1 Основные регистры:

  • r0-r3 – регистры аргументов и царапин; r0-r1 также являются результирующими регистрами
  • r4-r8 – регистры сохранения сбережений
  • r9 может быть регистром сохранения или нет (по некоторым вариантам AAPCS это специальный регистр)
  • r10-r11 – регистры с сохранением вызываемого абонента
  • r12-r15 являются специальными регистрами

Из AAPCS, §5.1.2.1 Соглашения об использовании регистров VFP:

  • s16-s31 (d8-d15, q4-q7) должны быть сохранены
  • s0-s15 (d0-d7, q0-q3) и d16-d31 (q8-q15) не нужно сохранять

Оригинальное сообщение:
arm-к-с-вызывающему-конференц-неон-регистров к сэкономить

Для 64-битного ARM, A64 (от стандартного вызова процедуры для 64-битной архитектуры ARM)

Есть тридцать один, 64-разрядный, универсальный (целочисленный) регистр, видимый для набора команд A64; они обозначены r0-r30 . В 64-битном контексте эти регистры обычно относятся к именам x0-x30 ; в 32-битном контексте регистры задаются с помощью w0-w30 . Кроме того, может быть использован регистр стека, SP , с ограниченным числом инструкций.

  • SP Указатель стека
  • r30 LR Регистр ссылок
  • r29 FP Указатель кадра
  • r19 … r28 Регистры, сохраненные Callee
  • r18 Регистр платформы, если это необходимо; в противном случае – временный регистр.
  • r17 IP1 Второй временной регистр временного вызова (может использоваться винирами вызовов и кодом PLT); в другое время может использоваться как временный регистр.
  • r16 IP0 Первый регистрационный регистр внутри процедуры (может использоваться винирами вызовов и кодом PLT); в другое время может использоваться как временный регистр.
  • r9 … r15 Временные регистры
  • r8 Непрямой регистр местоположения результата
  • r0 … r7 Регистры параметров / результатов

Первые восемь регистров, r0-r7 , используются для передачи значений аргументов в подпрограмму и для возврата значений результата из функции. Они также могут использоваться для хранения промежуточных значений в рамках процедуры (но, в общем, только между вызовами подпрограмм).

Регистры r16 (IP0) и r17 (IP1) могут использоваться компоновщиком как регистр нуля между обычной и любой подпрограммой, которую он вызывает. Они также могут использоваться в рамках процедуры для хранения промежуточных значений между вызовами подпрограмм.

Роль регистра r18 является специфичной для платформы. Если для платформы ABI необходим специальный регистр общего назначения для переноса межпроцедурного состояния (например, контекст streamа), то он должен использовать этот регистр для этой цели. Если платформа ABI не имеет таких требований, тогда она должна использовать r18 в качестве дополнительного временного регистра. Спецификация платформы ABI должна документировать использование этого регистра.

SIMD

64-битная архитектура ARM также имеет еще тридцать два регистра, v0-v31 , которые могут использоваться операциями SIMD и Floating-Point. Точное имя регистра изменится, указав размер доступа.

Примечание. В отличие от AArch32, в AArch64 128-битные и 64-битные представления регистра SIMD и Floating-Point не перекрывают несколько регистров в более узком представлении, поэтому q1, d1 и s1 все относятся к той же записи в регистре банка.

Первые восемь регистров, v0-v7 , используются для передачи значений аргументов в подпрограмму и для возврата значений результата из функции. Они также могут использоваться для хранения промежуточных значений в рамках процедуры (но, в общем, только между вызовами подпрограмм).

Регистры v8-v15 должны быть сохранены вызываемым вызовом под вызовом подпрограммы; остальные регистры ( v0-v7, v16-v31 ) не нужно сохранять (или должны быть сохранены вызывающим). Кроме того, необходимо сохранить только нижние 64-бит каждого значения, хранящегося в v8-v15 ; ответственность за сохранение больших ценностей лежит на вызывающем абоненте.

Ответы CesarB и Pavel предоставили цитаты из AAPCS, но остаются открытыми проблемы. Сохраняет ли заказчик r9? Как насчет r12? Как насчет r14? Кроме того, ответы были очень общими и не были специфическими для инструментальной цепочки arm-eabi в соответствии с запросом. Вот практический подход, чтобы выяснить, какой регистр спасен, а какие нет.

Следующий код C содержит встроенный блок сборки, который утверждает, что модифицирует регистры r0-r12 и r14. Компилятор будет генерировать код для сохранения регистров, требуемых ABI.

 void foo() { asm volatile ( "nop" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14"); } 

Используйте командную строку arm-eabi-gcc-4.7 -O2 -S -o - foo.c и добавьте ключи для своей платформы (например, -mcpu=arm7tdmi например). Команда будет печатать сгенерированный код сборки в STDOUT. Это может выглядеть примерно так:

 foo: stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} nop ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} bx lr 

Обратите внимание, что сгенерированный код компилятора сохраняет и восстанавливает r4-r11. Компилятор не сохраняет r0-r3, r12. То, что он восстанавливает r14 (alias lr), является чисто случайным, поскольку по опыту я знаю, что код выхода также может загрузить сохраненный lr в r0, а затем сделать «bx r0» вместо «bx lr». Либо добавив -mcpu=arm7tdmi -mno-thumb-interwork либо используя -mcpu=cortex-m4 -mthumb мы получим немного другой код сборки, который выглядит следующим образом:

 foo: stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} nop ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc} 

Опять же, r4-r11 сохраняются и восстанавливаются. Но r14 (alias lr) не восстанавливается.

Обобщить:

  • r0-r3 не спасены
  • r4-r11 сохранены в памяти
  • r12 (alias ip) не спасен
  • r13 (alias sp) спасен
  • r14 (alias lr) не спасен
  • r15 (alias pc) является программным счетчиком и устанавливается на значение lr до вызова функции

Это выполняется, по крайней мере, по умолчанию для arm-eabi-gcc. Существуют переключатели командной строки (в частности, переключатель -mabi), которые могут влиять на результаты.

Существует также разница, по крайней мере, в архитектуре Cortex M3 для вызова функции и прерывания.

Если произойдет прерывание, он сделает автоматический нажим R0-R3, R12, LR, ПК на стек и при возврате формы IRQ автоматически POP. Если вы используете другие регистры в процедуре IRQ, вам нужно вручную нажимать / выталкивать их на стек.

Я не думаю, что это автоматический PUSH и POP для вызова функции (команда перехода). Если соглашение говорит, что R0-R3 может использоваться только как регистр аргументов, результатов или царапин, поэтому нет необходимости хранить их перед вызовом функции, потому что не должно быть никакого значения, используемого позже после возврата функции. Но так же, как при прерывании, вы должны хранить все остальные регистры процессора, если используете их в своей функции.

Давайте будем гением компьютера.