Возвращение структур в регистрах - ARM ABI в GCC - PullRequest
4 голосов
/ 25 мая 2010

В документации ARM ABI я сталкиваюсь с функциями, определенными как:

__value_in_regs struct bar foo(int a, int b) {
    ...
}

, но GCC ( 4.3.3 ) не позволяет этого, и все, что я мог найти, это ссылки на какой-то RealView компилятор. Есть ли способ сделать это из GCC?

Я пробовал -freg-struct-return, но это не имеет значения. Поскольку это ABI, я не могу изменить исходные программы, и возвращение регулярной структуры приводит к повреждению стека.

Я бы предпочел не использовать сборку для этого, если этого можно избежать, так как в этом нет необходимости.

Спасибо!

Ответы [ 4 ]

5 голосов
/ 28 мая 2010

Публикация в качестве ответа на запрос:

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

  1. Вызовы из вашего двоичного файла в ABI другого двоичного файла.
  2. Вызовы из другого двоичного файла в ABI вашего двоичного файла.

Обе эти проблемы решаются одинаково. Чтобы вызывать из своего кода, вам нужно создать в сборке функции shim, которые изменяют соглашение о вызовах, чтобы соответствовать внешнему ABI, а затем вызывать внешние функции оттуда. Отличие от вашего кода C состоит в том, что теперь для внешних вызовов вы вызываете подпрограмму внутренней сборки, и она делает все, что нужно для внешнего вызова, а затем возвращает возвращаемое значение в формате, понятном для кода C, и возвращает .

Чтобы поддерживать вызовы из внешнего двоичного файла в вашего кода, вы делаете то же самое, но в обратном порядке. Точки входа в ваш двоичный файл будут небольшими процедурами сборки, которые преобразуют внешний ABI в формат, понятный вашему C-коду, вызывают вашу внутреннюю функцию, затем возвращают возвращаемые значения в формат, понятный внешнему коду, и возвращают.

Иногда, боюсь, просто нет хорошего решения.

3 голосов
/ 07 августа 2015

Вы можете сделать это для двух регистров, используя "long long", как указано в ссылке "Стандарт вызова процедур для архитектуры ARM", приведенной на этой странице.

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, используя "Containerized векторы ":

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.

0 голосов
/ 15 марта 2011

"Стандарт вызова процедур для архитектуры ARM" специально говорит (раздел 5.4: Возврат результата):

"Составной тип не более 4 байтов возвращается в R0."

"Составной тип размером более 4 байт ... сохраняется в памяти по адресу, переданному в качестве дополнительного аргумента при вызове функции ...."

Я знаю, что у некоторых процессоров есть несколько разных "стандартных" ABI. Но у меня сложилось впечатление, что практически все компиляторы для ARM использовали один и тот же ABI.

У вас есть доказательства того, что GCC не не использует этот стандартный ABI?

Не могли бы вы опубликовать ссылку на любую информацию о ABI для ARM, которая отличается от этого стандартного ABI - ABI, используемый вызывающим абонентом или вызываемым, или обоими?

0 голосов
/ 25 мая 2010

Я не уверен, что это сработает, но вы можете попробовать использовать атрибут функции pcs :

struct bar foo(int a, int b) __attribute__((pcs("aapcs")));
struct bar foo(int a, int b) {
    ...
}
...