Ответ Тимоти Болдуина точен, но я подумал, что немного расширю его.Двоичный интерфейс приложения ARM (ABI) и, в частности, стандарт вызова процедуры , определяет, как обрабатывается каждый регистр через границы вызова процедуры.
В итоге, r0-r3
используется в качестве аргументаи передача возвращаемого значения, и она должна быть сохранена вызывающим вместе с «регистром обработки внутренних вызовов» r12
.Все остальные регистры общего назначения должны сохраняться вызываемой .Поэтому, если вы пишете свои собственные функции на языке ассемблера, вы должны нажать любой из r4-r11
, который вы используете, в дополнение к lr
(r14
), если вы вызываете любые другие функции, и вы должны убедиться, что вы используетестек «сбалансирован», так что sp
(r13
) имеет то же значение на выходе, что и на входе.
Большинство компиляторов (по крайней мере, с отключенными оптимизациями) генерируют код, отвечающий этим требованиям, перемещаялюбые аргументы функции из r0-r3
в самом начале, сразу после нажатия регистров сохранения вызываемого объекта;путем перемещения любого возвращаемого значения в r0
непосредственно перед возвратом;и используя r0-r3
только для передачи аргументов вызываемым функциям и из них во время выполнения функции.
Кроме того, для всех функций в разных объектных файлах (т.е. в разных исходных файлах) стек должен быть выровнен по 8 байтов,и это хорошая привычка всегда поддерживать 8-байтовое выравнивание, потому что это легко сделать и это бесплатно (по причинам, связанным с кешем).Для этого убедитесь, что каждое взаимодействие со стеком (каждые PUSH
и POP
) включает в себя четное количество регистров, добавляя и выдвигая дополнительные регистры, если необходимо составить четное число.