Добавление значений в C с использованием встроенной сборки

Я пытаюсь понять основы встроенной сборки в C (сборка ATT), поэтому я тренируюсь, добавляя 2 переменные.

Итак, это работает как задумано; переменная src копируется в переменную dst, а затем переменная dst добавляется на 5. Значения src и dst равны 1 и 6 соответственно.

int src = 1;
int dst = 0;

asm ("mov %[SRC], %[DEST]\n\t"
     "add $5, %0"
     : [DEST] "=r" (dst));
     : [SRC] "r" (src));

Но когда я пытаюсь это сделать, значения src и dst по-прежнему равны 1 и 6. Я ожидал, что src будет иметь значение 1, а dst будет иметь значение 5 с момента добавления 5 к dst (который имеет значение 0, поскольку MOV операция была удалена) должна иметь выход 5.

int src = 1; int dst = 0;

 asm ("add $5, %[DEST]"
     : [DEST] "=r" (dst)
     : [SRC] "r" (src));

Затем я пытаюсь удалить src в качестве входного операнда, используя следующий код, но теперь dst получает значение 11.

int dst = 0;

     asm (
         "add $5, %[DEST]"
         : [DEST] "=r" (dst));

Теперь я немного запутался, как это работает. Что я недопонимаю?

1 ответ

Решение

Первая часть вашего кода работает как положено. Там

 mov %[SRC], %[DEST]        ; copies %[SRC] into %[DEST], which is now 1
 add $5, %0                 ; adds 5 to %0 (which is %[DEST]), so that's 6

Вторая часть не работает, потому что вы никогда не используете %[SRC], и потому что %[DEST] не является входным операндом, поэтому его значение не входит в расчет. Вы просто получаете то, что происходит в реестре, который gcc решает использовать. Третья часть терпит неудачу по той же причине.

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

asm("add $5, %0"  // This does not work!
    : "=r" (dst)
    : "r" (dst));

потому что теперь у вас есть входной операнд %1 со значением dst и отличный выходной операнд %0 чье значение будет записано в dstи вы никогда не используете %1, Эта запись позволит вам написать

asm("mov %1, %0; add $5, %0"  // needlessly inefficient!
    : "=r" (dst)
    : "r" (dst));

но это, конечно, излишне неэффективно. Чтобы сделать это с одним регистром, вам нужно использовать соответствующее ограничение:

asm("add $5, %0"
    : "=r" (dst)
    : "0" (dst));

Это говорит GCC, что %0 как входной операнд разрешен, и что он имеет значение dst, Вот соответствующая часть руководства gcc.

Наконец, с именованными операндами это выглядит так:

asm ("add $5, %[DEST]"
     : [DEST] "=r" (dst)
     : "[DEST]" (dst));
Другие вопросы по тегам