Какие регистры нужно сохранить в соглашении о вызовах ARM C? - PullRequest
48 голосов
/ 04 ноября 2008

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

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

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

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

Ответы [ 5 ]

66 голосов
/ 04 ноября 2008

Это зависит от 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, и его можно изменить даже в командной строке. Глядя на код пролога / эпилога, который он генерирует, не так уж полезно, поскольку он адаптирован для каждой функции и , и компилятор может использовать другие способы сохранения регистра (например, сохранение его в середине функции ).

22 голосов
/ 29 марта 2011

Для добавления отсутствующей информации о регистрах NEON:

С AAPCS , §5.1.1 Регистры ядра:

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

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

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

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

16 голосов
/ 13 апреля 2015

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

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

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

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

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

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

SIMD

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

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

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

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

6 голосов
/ 13 августа 2013

Ответы 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 (псевдоним 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 (псевдоним lr) не восстанавливается.

Подведем итог:

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

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

0 голосов
/ 13 июля 2017

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

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

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

...