ошибка «неопределенный именованный операнд» во встроенной сборке arm64

#assembly #gcc #inline-assembly #arm64

#сборка #gcc #встроенная сборка #arm64

Вопрос:

Это код, который я использовал для записи некоторых данных в память для отладки (пока printf не будет доступен в программе u-boot). Переменная myptr находится в .__mydebug разделе и увеличивается на 8 после каждой 8-байтовой записи, и я хочу записать любое значение, которое меня интересует, в виде {debug_tag, debug_value} пары. Вот debug_tag некоторое значение для отображения последовательности данных отладки, и это debug_value значение, которое я хочу проверить (или увидеть) во время отладки. Это сборка arm64.

 .global myptr

ldr x28, =myptr   /* load the address of myptr */
add x28, x28, #8  /* set write pointer to the next address after the myptr variable */
mov x27, #0x33   /* first debug write starts with tag value 0x33 */
str x27, [x28], #8   /* write the tag value, increment the pointer */
mov x27, some_value   /* some_value : the value I want to see with tag value 0x33 */
str x27, [x28], #8   /* write the debug value, increment the pointer */    ldr x26, =myptr    /* load pointer address */


/* next debug write, in the same assembly code, x28 hasn't changed, so use as is */
mov x27, #0x34  /* new debug tag */
str x27, [x28], #8  /* write new tag, increment pointer */
mov x27, some_another_value  /* another data I want to check */
str x27, [x28], #8  /* write the data, increment pointer */
ldr x26, =myptr  /* load the address of myptr to x26 */
str x28, [x26]   /* save the updated pointer in myptr, just in case x28 is modified and 
                   the pointer should be used later in assembly or C code .. */
.... (skip) ....

.section .__mydebug
myptr: .double 0x0
data_start: .double 0x0
 

Таким образом, это последовательная запись отладочной информации в раздел памяти, который работает.
Я могу продолжить эту отладочную запись позже в программе .c, как показано ниже, и это также работает.

 // debug print 
int xx=sizeof(struct global_data);
*((uint64_t *)myptr) = 0x101; myptr =8;  /* debug tag start with 0x101 here */
*((uint64_t *)myptr) = xx; myptr =8;     /* write some data I want to check.. */
*((uint64_t *)myptr) = 0x102; myptr =8;  /* another debug tag */
*((uint64_t *)myptr) = base; myptr =8;    /* another value I want to check */
 

Хорошо, я могу с этим смириться. Но это выглядит неприятно и неудобно.
Итак, мне любопытно, как я могу сделать это в программе C, используя функцию со встроенной сборкой. Я хочу передать функции значение тега и значение отладки (64-разрядное) в качестве аргументов. Функция должна извлекать myptr значение для записи тега и данных и обновлять myptr значение каждый раз. Я попытался написать функцию ниже.

 void dbg_print(unsigned int tag, uint64_t data)
{
    uint64_t ptr_addr1;
__asm (
    "ldr %[ptr_addr], =myptr" 
    "ldr %[ptr_val], [%[ptr_addr]]" 
    "str %[tag_val], [%[ptr_val]], #8" 
    "str %[data_val], [%[ptr_val]], #8"
    : /* no output */ 
    : [tag_val] "r" (tag), [data_val] "r" (data) /* input list */ 
    : "memory" /* no specific clobbered register, but memory modified */
);
}
 

Когда я его компилирую, я получаю эту ошибку компиляции.

 common/init/board_init.c: In function 'dbg_print':
common/init/board_init.c:144:1: error: undefined named operand 'ptr_addr'
  144 | );
      | ^
common/init/board_init.c:144:1: error: undefined named operand 'ptr_val'
common/init/board_init.c:144:1: error: undefined named operand 'ptr_addr'
common/init/board_init.c:144:1: error: undefined named operand 'ptr_val'
common/init/board_init.c:144:1: error: undefined named operand 'ptr_val'
make[2]: *** [scripts/Makefile.build:254: spl/common/init/board_init.o] Error 1
make[1]: *** [scripts/Makefile.spl:515: spl/common/init] Error 2
 

Я не могу понять undefined named operand ошибку. Нужно ли мне где-то определять операнд в шаблоне? В примере в https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_qbn1517569205870.htm , операнды в шаблоне просто используются без определения. Переменные в C объявляются в любом случае, но разве операнды в шаблоне сборки не заменяются компилятором в любом случае?
Спасибо за чтение, и я был бы признателен, если бы кто-нибудь мог разъяснить мне эту вещь.

Добавить :
Прочитав комментарии Нейта Элдриджа и Питера Кордеса, я понял, что «определение операнда» означает подключение значения к миру C с использованием спецификаторов операндов (эти поля после : и связаны с :). Итак, я попытался изменить код на это, и я посмотрю, работает ли это.

 void dbg_print(unsigned int tag, uint64_t data)
{
    uint64_t ptra, ptrv;
__asm (
    "ldr %[ptr_addr], =myptr n"   /* get pointer address */
    "ldr %[ptr_val], [%[ptr_addr]] n"    /* get pointer value */
    "str %[tag_val], [%[ptr_val]], #8 n"   /* write to pointed addr */
    "str %[data_val], [%[ptr_val]], #8 n"   /* write to pointed addr */
    : [ptr_addr] "=r" (ptra), [ptr_val] "=r" (ptrv)
    : [tag_val] "r" (tag), [data_val] "r" (data) /* input list */
    : "memory" /* no specific clobbered register, but memory modified */
    );
}
 

Комментарии:

1. Я не уверен, почему вы хотите использовать встроенную сборку для простых загрузок и хранилищ, с которыми компилятор может справиться отлично. Вы могли бы записать его более чисто volatile uint64_t *p = (volatile uint64_t *)myptr; p[0] = 0x101; p[1] = xx; p[2] = 0x102; p[3] = base; или обернуть в красивую функцию или макрос.

2. В примере операнды в шаблоне НЕ просто используются без определения; имена lhs и rhs в шаблоне сопоставляются с именами, указанными для операндов в конце. В вашем коде не определен ни один вызываемый операнд ptr_addr , поэтому он не работает. Он не будет автоматически сопоставляться с программной переменной с таким именем, если это то, что вам интересно.

3. На самом деле должно быть volatile uint64_t *p = (volatile uint64_t *)amp;myptr; , если я правильно понимаю код.

4. Указанные вами именованные операнды — это [data_val] and [tag_val] , а не [ptr_addr] and [ptr_val] , поэтому сообщение об ошибке кажется довольно очевидным; GCC не знает, как развернуть %[ptr_addr] , потому что вы не дали ни одному из операндов это имя в расширенной инструкции Asm. Как говорит Нейт, лучшим решением, вероятно, является полная замена инструкции inline-asm. Тем более, что у вас есть ошибки, такие как запись регистра без указания каких-либо "=r" выходных операндов, только "r" входные данные (что подразумевает доступ только для чтения).

5. Привет, Нейт и Питер, спасибо за комментарии. Я получал от вас помощь много раз раньше ( :)) . Я обновил свой вопрос и попробую, если это сработает. кстати, я просто хотел использовать функцию, использующую встроенную сборку для этого случая. (для более четкого понимания).

Ответ №1:

Я не могу понять ошибку неопределенного именованного операнда. Нужно ли мне где-то определять операнд в шаблоне? В примере в https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_qbn1517569205870.htm , операнды в шаблоне просто используются без определения.

Да, вам нужно определить их либо во входных, либо в выходных данных инструкции asm. Все примеры, которые вы связываете, определяют все имена, которые они используют. Ваш код просто определяет tag_val и data_val там, поэтому ptr_val и ptr_addr не определены.

Комментарии:

1. Я узнал об этом из комментария Питера Кордеса, но поскольку это был оригинальный вопрос, я выбираю его в качестве ответа. Спасибо!