Возврат структур в регистры-ARM ABI в GCC
В документации ARM ABI я встречаю функции, определенные как:
__value_in_regs struct bar foo(int a, int b) {
...
}
Но ССЗ(4.3.3) не позволяет, и все, что я смог найти, - это ссылки на какой-то компилятор RealView. Есть ли какой-либо способ сделать это из GCC?
Я пробовал-freg-struct-return, но это не имеет значения. Поскольку это ABI, я не могу изменить исходные программы, а возвращение обычной структуры искажает стек.
Я бы предпочел не использовать сборку для этого, если этого можно избежать поскольку иначе и не надо.
Спасибо!
4 ответа:
Размещение в качестве ответа по запросу:
Если вам нужно сгенерировать двоичный файл, который будет работать с ABI, который ваш компилятор не поддерживает, у вас будут некоторые проблемы. В этом случае вам придется вернуться к программированию на языке ассемблера и выполнить необходимые вызовы. Есть две возможности:
- вызовы с вашего двоичного кода в другой двоичный Эби.
- вызовы из другого двоичного файла в ABI вашего двоичного файла.
Оба из них задачи решаются аналогично. Чтобы вызвать его из кода, вам нужно создать в сборке функции shim, которые будут вращаться вокруг соглашения о вызове, чтобы соответствовать внешнему ABI, а затем вызывать внешние функции оттуда. Разница для вашего кода C заключается в том, что теперь для выполнения внешних вызовов вы вызываете внутреннюю процедуру сборки, и она делает все, что ей нужно, чтобы вызвать внешний вызов, затем помещает возвращаемое значение обратно в формат, который будет понятен вашему коду C, и возвращает.
Для поддержки вызывая из внешнего двоичного файла в ваш код, вы делаете то же самое, но в обратном порядке. Точками входа в ваш двоичный файл будут небольшие сборочные процедуры, которые преобразуют внешний ABI в формат, понятный вашему C-коду, вызывают вашу внутреннюю функцию, затем помещают возвращаемые значения обратно в формат, понятный внешнему коду, и возвращают.
Иногда просто нет хорошего решения, я боюсь.
Вы можете сделать это для двух регистров, используя "long long", как указано в ссылке "Procedure Call Standard for the ARM Architecture", приведенной на этой странице.
long long test(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { long long ret; ret = a+b; ret <<= 32; ret |= c + d; return ret; }
Будет просто скомпилирован как:
0002dbb8 <test>: 2dbb8: 1841 adds r1, r0, r1 2dbba: 18d0 adds r0, r2, r3 2dbbc: 4770 bx lr
И
ret & 0xFFFFFFFF
иret >> 32
в вызывающей функции будут легко заменены на r0 и r1.Это даже можно сделать для регистров r0 - r3, используя "Контейнеризированный векторы":
typedef uint32_t uint32x4_t __attribute__ ((vector_size (16))); uint32x4_t test2(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { uint32x4_t ret = { a + 1, b + 2, c + 3, d + 4}; // to access elements: ret[0], ret[1], ... return ret; }
Который составляется следующим образом:
0002dbb8 <test2>: 2dbb8: 3001 adds r0, #1 2dbba: 3102 adds r1, #2 2dbbc: 3203 adds r2, #3 2dbbe: 3304 adds r3, #4 2dbc0: 4770 bx lr
Обратите внимание, что на него ссылаются как на Функция SIMD / NEON в документе выше, но я только что достиг ее на Cortex M0 в режиме Thumb, без поддержки NEON.
"стандарт вызова процедур для архитектуры ARM" в частности, говорится (раздел 5.4: возврат результата):
" в R0 возвращается составной тип размером не более 4 байт."
" составной тип размером более 4 байт ... хранится в памяти по адресу, переданному в качестве дополнительного аргумента при вызове функции ... ."
Я знаю, что некоторые процессоры имеют несколько различных" стандартных " ABI. Но у меня сложилось впечатление, что практически все компиляторы для ARM использовали это же Эби.
Есть ли у вас какие-либо доказательства того, что GCC не использует этот стандартный ABI?
Не могли бы вы разместить ссылку на любую информацию о ABI для ARM, которая отличается от этой стандартной ABI-ABI, используемой вызывающим, или вызываемым, или обоими?
Я не уверен, что это сработает, но вы можете попробовать использовать
pcs
атрибут функции :struct bar foo(int a, int b) __attribute__((pcs("aapcs"))); struct bar foo(int a, int b) { ... }