ASM inline сомнение - PullRequest
       1

ASM inline сомнение

5 голосов
/ 21 августа 2011

Я пытаюсь понять кое-что о встроенном ассемблере в Linux. Я использую следующую функцию:

void test_func(Word32 *var){
   asm( " addl %0, %%eax" : : "m"(var) );
   return;
}

Генерирует следующий код ассемблера:

.globl test_func
.type   test_func, @function
test_func:
        pushl %ebp
        movl %esp, %ebp
#APP
# 336 "opers.c" 1
        addl 8(%ebp), %eax
# 0 "" 2
#NO_APP
        popl %ebp
        ret
        .size   test_func, .-test_func

Суммирует адрес var mem в значение регистра eax вместо значения var.

Можно ли как-то указать команде addl использовать значение var вместо адреса var mem без копирования адреса var mem в регистр?

Привет

Ответы [ 4 ]

4 голосов
/ 21 августа 2011

Да, потому что вы даете ему вар, который является адресом. дай ему * вар.

как:

void test_func(Word32 *var){
   asm( " addl %0, %%eax" : : "m"(*var) );
   return;
}

Я точно не помню, но вы должны заменить "m" на "r"?

Операнд памяти не означает, что он будет принимать значение с этого адреса. это просто указатель

3 голосов
/ 22 августа 2011

Суммирует адрес переменной памяти в значение регистра eax вместо значения переменной.

Да, синтаксис встроенной сборки gcc довольно загадочный. Если перефразировать соответствующий раздел * HOWTO "m", встроенного в сборку GCC, то приблизительно даст вам место в памяти для переменной C.

Это то, что вы используете, когда вам нужен адрес, по которому вы можете писать или читать. Заметьте, я сказал, что местоположение переменной C , поэтому %0 установлено на адрес Word32 *var - у вас есть указатель на указатель. Трансляция C встроенного блока сборки может выглядеть как EAX += *(&var), потому что вы можете сказать, что ограничение "m" неявно берет адрес переменной C и дает вам адресное выражение, которое вы затем добавляете к %eax.

Есть ли способ указать команде addl использовать значение var вместо адреса var mem без копирования адреса var mem в регистр?

Это зависит от того, что вы имеете в виду. Вам нужно получить var из стека, поэтому кто-то должен разыменовать память (см. Ответ @Bo Perssons), но вам не нужно делать это во встроенной сборке

Ограничение должно быть "m"(*var) (как предложено @fazo). Это даст вам область памяти значения, которое var указывает на , а не область памяти, указывающую на нее.

Сгенерированный код теперь:

test_func:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
#APP
# 2 "test.c" 1
    addl    (%eax), %eax
# 0 "" 2
#NO_APP
    popl    %ebp
    ret

Это немного подозрительно, но это понятно, так как вы забыли сообщить GCC, что вы засорились (изменено без наличия в списке ввода / вывода) %eax. Исправление, которое asm("addl %0, %%eax" : : "m"(*var) : "%eax" ) генерирует:

    movl    8(%ebp), %edx
    addl    (%edx), %eax

Что не лучше и не правильнее в этом случае, но всегда полезно помнить. См. Раздел в списке clobber и обратите особое внимание на "memory" clobber для расширенного использования встроенной сборки.

Даже если вы не хотите (явно) загружать адрес памяти в регистр, я кратко расскажу о нем. Изменение ограничения с "m" на "r" почти работает, соответствующие разделы изменяются на (если мы включим %eax в список clobber):

    movl    8(%ebp), %edx
    addl    %edx, %eax

Что почти правильно, мы загрузили значение указателя var в регистр, но теперь мы должны указать сами, что загружаем из памяти. Изменение кода в соответствии с ограничением (обычно нежелательно, я показываю его только для полноты):

asm("addl (%0), %%eax" : : "r"(var) : "%eax" );

Дает:

movl    8(%ebp), %edx
addl    (%edx), %eax

Так же, как с "m".

1 голос
/ 21 августа 2011

Нет, для процессоров x86 нет режима адресации, который бы передавал два уровня косвенно.

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

0 голосов
/ 22 августа 2011

Попробуйте

void test_func(Word32 *var){
    asm( " mov %0, %%edx; \
    addl (%%edx), %%eax" : : "m"(var) );    

    return;
} 
...