У меня есть этот код, который отлично работает:
void function( void )
{
__asm volatile
(
" ldr r3, .ADDRESS \n"
" mov r2, %0 \n"
" str r2, [r3, %1] \n"
".ADDRESS: .word 0x401C4000 \n"
:: "i" (1<<17), "i" (16)
);
}
Но для объявления .ADDRESS
я использовал магическое число 0x401C4000. У меня действительно есть макрос для этого.
Например:
#define ADDR_BASE 0x401C4000
#define ADDR ((void *)ADDR_BASE)
void function( void )
{
__asm volatile
(
" ldr r3, .ADDRESS \n"
" mov r2, %0 \n"
" str r2, [r3, %1] \n"
".ADDRESS: .word %2 \n"
:: "i" (1<<17), "i" (16), "i" (ADDR)
);
}
Это не строит.
Как я могу использовать макрос в этом случае?
@PeterCordes это вы должны рассматривать только как пример моей проблемы. Предположим, что мне нужно изменить эту функцию github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/portable/GCC/…. Как видите, я не могу написать это на c, а вместо этого мне нужно встроить эти 3 инструкции в пару точек
Если у вас есть какие-то инструкции, которые компилятор не может сгенерировать, да, для этого и нужен встроенный ассемблер. Но обычно вы хотите, чтобы Только эти инструкции находились внутри вашего ассемблерного шаблона, сообщая компилятору, что вы хотите в регистрах, прежде чем они запустятся. Ничто не мешает вам сократить это до asm ("str %0, [%2, %1]" :: "r" (1<<17), "i" (16), "r" (ADDR) : "memory");
с ограничениями регистров для двух вещей, которые вам нужны в регистрах, поэтому ваш ассемблерный шаблон не записывает никаких регистров и, следовательно, не нуждается в стирании регистров. И не иметь .word
в пути выполнения без .pushsection/.popsection)
См. правку ниже. Я оставляю это первое решение только в качестве примера того, что вам не следует делать.
Я нашел этот сообщение, где дается решение моей той же проблемы, но оно связано с x86. Я все равно хотел попробовать, и это работает.
Затем используйте %c2
вместо %2
, как показано здесь:
#define ADDR_BASE 0x401C4000
#define ADDR ((void *)0x401C4000)
void function( void )
{
__asm volatile
(
" ldr r3, .ADDRESS \n"
" mov r2, %0 \n"
" str r2, [r3, %1] \n"
".ADDRESS: .word %c2 \n"
:: "i" (1<<17), "i" (16), "i" (ADDR): "r2", "r3", "memory"
);
}
РЕДАКТИРОВАТЬ
Решение, которое я предложил выше, может не работать, видя сгенерированный код:
20 function:
21 @ Function supports interworking.
22 @ args = 0, pretend = 0, frame = 0
23 @ frame_needed = 1, uses_anonymous_args = 0
24 @ link register save eliminated.
25 0000 04B02DE5 str fp, [sp, #-4]!
26 0004 00B08DE2 add fp, sp, #0
27 .syntax divided
28 @ 6 "test.c" 1
29 0008 04309FE5 ldr r3, .ADDRESS
30 000c 0228A0E3 mov r2, #131072
31 0010 102083E5 str r2, [r3, #16]
32 0014 00401C40 .ADDRESS: .word 1075593216
33
34 @ 0 "" 2
35 .arm
36 .syntax unified
37 0018 0000A0E1 nop
38 001c 00D08BE2 add sp, fp, #0
39 @ sp needed
40 0020 04B09DE4 ldr fp, [sp], #4
41 0024 1EFF2FE1 bx lr
Вы можете видеть, что в строке 32 символ .ADDRESS
был выдан, но он окружен кодом, и нет инструкции его пропустить. Я считаю, что его можно попытаться выполнить, как если бы это была инструкция.
Возможно, лучшее решение, предложенное Питером,
#define ADDR_BASE 0x401C4000
#define ADDR ((void *)0x401C4000)
void function( void )
{
__asm volatile
(
"str %0, [%2, %1]"
:: "r" (1<<17), "i" (16), "r" (ADDR) : "memory"
);
}
Поскольку цель этого сайта — архивировать вопросы и ответы, которые будут полезны будущим пользователям, не могли бы вы исправить ошибки в коде, который вы разместили, в частности, отсутствующие clobbers? Эта концепция достаточно сложна для того, чтобы новые программисты поняли ее правильно, не будучи завалены неправильным кодом через Интернет.
@NateEldredge Я отредактировал, добавив клобберы. Думаю правильно написал. В ссылке, из которой я взял пример, их не было.
Удалил мой отрицательный голос, поскольку единственная оставшаяся ошибка — это демонстрация, где .word
находится прямо в разделе .text
и выполняется как байты инструкции. Люди не будут копировать это и думать, что это работает, хотя на самом деле это небезопасно; скорее всего просто рухнет. (Если только .word
не декодируется как инструкция, которая искажает другие регистры). На сегодняшний день лучшим решением было бы запросить "r"(ADDR)
у компилятора вместо того, чтобы материализовать его самостоятельно, тогда вы можете позволить ему выбрать регистр и не нуждаться в уничтожении. И пусть он запланирует загрузку раньше.
Вы намеревались поместить
.word
туда, где он будет выполняться как машинный код, сразу после инструкцииstr
? Кроме того, вы знаете, что можете запросить номер или адрес уже в реестре, верно? Если вы используете его только для загрузки или перемещения в регистр, вы просто делаете вещи менее эффективными, не позволяя компилятору потенциально оптимизировать после встраивания этой функции в цикл или что-то в этом роде. Кроме того, вы забыли объявления clobber на R2 и R3 и"memory"
, если там есть объект C, что было бы ненужным, если бы вы полностью избегали встроенного ассемблера и использовалиvolatile int*
.