Документация по встроенному ассемблеру 32-битного gcc (avr32-gcc) Atmel?

Мне нужно реализовать небольшой фрагмент кода в ассемблере для 32-битного AVR (тестирование памяти ОЗУ под работающей программой C, другого способа ее решить нет), однако я не могу найти какую-либо документацию по спецификациям AVR-32 встроенный ассемблер и метод проб и ошибок ни привели меня к успеху.

Первое: кто-нибудь знает о каких-либо документах, описывающих особенности AVR-32 встроенного ASM? (В частности, спецификации регистра ввода / вывода)

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

int ret;
int ad0;
int ad1;
/* ... */
__asm__ volatile(
 "mov     r2,  %0  \n\t"
 "mov     r3,  %1  \n\t"
 "mov     %2,  0   \n\t"
: "=r"(ret)
: "r"(ad0), "r"(ad1)
: "r2", "r3"
);

Скомпилированный с оптимизацией с использованием avr32-gcc, он производит следующий вывод сборки (-S):

#APP
#  95 "svramt.c" 1
 mov     r2,  r8  
 mov     r3,  r8  
 mov     r9,  0   

#  0 "" 2
#NO_APP

Обратите внимание, как%0 и%1 отображаются в один и тот же регистр (r8). Это, кажется, происходит, если присутствует выходной регистр. Чтобы проверить, правильно ли я использовал встроенную сборку, я также попробовал X86 с нативным gcc на хосте:

int ret;
int ad0;
int ad1;
/* ... */
__asm__ volatile(
 "mov     %0,    %%eax \n\t"
 "mov     %1,    %%ebx \n\t"
 "mov     0,     %2,   \n\t"
: "=r"(ret)
: "r"(ad0), "r"(ad1)
: "eax", "ebx"
);

Этот фрагмент компилируется в:

#APP
# 7 "asmtest.c" 1
 mov     %esi,    %eax 
 mov     %edx,    %ebx 
 mov     0,     %ecx,   

# 0 "" 2
#NO_APP

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

Мне бы хотелось обойти эту проблему (если это ошибка в avr32-gcc), указав регистры напрямую (пробуя "=r8" и такие как спецификации ввода / вывода), но это не компилируется таким образом.

Если нет документации, кто-нибудь знает, где спецификации источника для встроенного asm можно найти в ("нормальном" x86 или ARM) источнике GCC? Это стоило бы попробовать, но GCC - огромный зверь, который может пробраться без каких-либо предварительных знаний.

Я не верю, что у меня достаточно кармы, чтобы сделать это с помощью модуля ASM (с почти нулевым знанием сборки AVR-32), и для этого, по крайней мере, потребуется документация соглашения о вызовах, которую я до сих пор не нашел...,

РЕДАКТИРОВАТЬ: дальнейшие эксперименты показали, что с помощью =&r для выходного спецификатора, кажется, решить проблему отображения регистра. Почему так, я не в курсе (два входа сопоставлены с одним и тем же регистром). По крайней мере, вещь может быть проверена, поскольку она теперь производит намеченный фрагмент сборки.

Дальнейшие исследования выявили этот 8-битный документ AVR, который предлагает часть решения путем описания квадратных скобок для предоставления имен для операндов, имена которых могут использоваться во фрагменте сборки. Это устраняет возможную неопределенность, между которой операнд будет отображаться в какой %n уточнение во фрагменте. (Я не мог видеть этот синтаксис, описанный в других документах, но также работает в avr32-gcc, поэтому был полезен)

1 ответ

Возьмем следующий фрагмент кода:

          __asm__ volatile(
     "mov     r2,  %0  \n\t"
     "mov     r3,  %1  \n\t"
     "mov     %2,  0   \n\t"
    : "=r"(ret)
    : "r"(ad0), "r"(ad1)
    : "r2", "r3"
    );

Обратите внимание, как %0 и %1 отображаются на один и тот же регистр (r8).

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

Это означает, что gcc может распределять регистры таким образом, что выходные данные перекрывают входные данные. Если вы начнете записывать выходные данные до того, как все входные данные будут прочитаны, то — в зависимости от выбора регистра — вы можете переопределить (затереть) входные данные. Такая ситуация называется Early-clobber , и для того, чтобы сообщить компилятору, что это может произойти, существует модификатор ограничения&"раннее уничтожение" для выходных операндов.

Кроме того, вы описали op0 как выход, а op1 и op2 как входы 1 , но шаблон asm использует op0 и op1 как входы, а op2 как выход. Следовательно, операнды, которые соответствуют фактическому ассемблерному коду:

          __asm__ volatile(
     "mov     r2,  %1  \n\t"
     "mov     r3,  %2  \n\t"
     "mov     %0,  0   \n\t"
    : "=r" (ret)           /* output(s) */ 
    : "r" (ad0), "r" (ad1) /* input(s) */
    : "r2", "r3"
    );

Обратите внимание, что спецификатор "really-clobber" не требуется, потому что вы потребляете все входные данные перед записью выходных данных, и это нормально, еслиregперекрываетсяad0илиad1.

Дальнейшие эксперименты показали, что использование =&r для выходного спецификатора, по-видимому, решает проблему отображения регистров. Почему так, я не в курсе

Описание выходного операнда как «раннее затирание» означает, что компилятор должен использовать новый регистр для выходного операнда, чтобы он не перекрывал входной операнд. В исходном коде вы использовали входные данные вместо выходных и т. д., но=&rдля вывода, который на самом деле является вводом, все равно исправит ситуацию с ранним забиванием (но встроенный ассемблер все равно будет неверным).

Я также попробовал X86 с собственным gcc на хосте [...] Этот фрагмент компилируется в: [...] Это то, что я ожидал от аналога AVR-32,

Тот факт, что ассемблерный код, сгенерированный некоторым встроенным ассемблером (или любым другим кодом в этом отношении), соответствует вашим ожиданиям, еще не означает, что встроенный ассемблер корректен. Неправильный встроенный ассемблер (или другой код в этом отношении) может по-прежнему генерировать ожидаемый результат сборки.Вы не можете использовать это, чтобы доказать правильность кода. Только когда сгенерированный код не такой, как ожидалось, вы знаете, что источники неверны (или есть редкий случай ошибки инструмента).

Мне бы хотелось обойти проблему [...], указав регистры напрямую (попробовав "=r8" и такие как спецификации ввода/вывода), но это не компилируется таким образом.

Строки ограничений должны содержать: ограничения."=r8"означает "выходной операнд, который находится в классе регистров "r" или соответствует номеру операнда 8" . Это не имеет никакого смысла. Что вы можете сделать, так это использовать переменные локального регистра, как показано ниже, но, скорее всего, это не то, что вам нужно:

          register int reg8 asm ("r8") = 42;
    int ret;
    asm  ("mov %0, %1" : "=r" (ret) : "r" (reg8));

Это предполагает допустимое имя регистра для цели. Кроме того, вы можете использовать номер регистра, как вreg8 asm ("8")предоставил регистрационный номерr88.


1 Я вообще не знаком с AVR32 asm, поэтому предположил семантикуmovинструкцияmov dest, source.

Другие вопросы по тегам