Возврат структур в регистры-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 4
gcc

4 ответа:

Размещение в качестве ответа по запросу:

Если вам нужно сгенерировать двоичный файл, который будет работать с ABI, который ваш компилятор не поддерживает, у вас будут некоторые проблемы. В этом случае вам придется вернуться к программированию на языке ассемблера и выполнить необходимые вызовы. Есть две возможности:

  1. вызовы с вашего двоичного кода в другой двоичный Эби.
  2. вызовы из другого двоичного файла в 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) {
    ...
}