2008-11-04 4 views
44

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

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

Я понимаю, что могу читать весь EABI, но затем сокращение RTFM - это то, для чего SO, для чего? :-)

+1

Это внешняя ссылка, которая может быть полезна. [APCS intro] (http://www.heyrick.co.uk/assembler/apcsintro.html), особенно некоторые [разные имена] (http://sourceware.org/ml/binutils/2000-06/msg00240.html) для использования `register`. – 2013-04-15 18:59:53

ответ

59

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

От the AAPCS, §5.1.1:

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

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

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

+0

Спасибо, это похоже на звон колоколов. Я думаю, что первый «r0-r4» в вашем списке - опечатка, верно? +1 (и, вероятно, лучший ответ, если не будет радикального поворота) – richq 2008-11-04 10:52:18

+0

Да, это была опечатка (и не единственная, но я исправил другие, прежде чем нажимать подать в первый раз - или я надеюсь). – CesarB 2008-11-04 11:02:52

21

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

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

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

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

  • S16-S31 (d8-d15, Q4-В7) должны быть сохранены
  • s0-s15 (D0-D7, q0-q3) и d16-d31 (q8-q15) не должны быть сохранены

Оригинальное сообщение:
arm-to-c-calling-convention-neon-registers-to-save

4

Ответы 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 (псевдоним IP) являетесь не вызываемых сохраняемым
  • r13 (alias sp) сохранен в обращении
  • r14 (alias lr) is не callee-saved
  • r15 (псевдоним ПК) счетчик программы и устанавливается на значение ЛО до вызова функции

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

13

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

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

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

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

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

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

SIMD

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

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

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

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

0

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

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

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