Почему выдача пустых команд asm меняет местами переменные? - PullRequest
4 голосов
/ 03 ноября 2019

Так что я возился со встроенной сборкой и скомпилировал ее, используя GCC 9 . В результате две переменные a и b были поменяны местами без непосредственного выполнения каких-либо прямых команд.

#include<cstdio>
int main(){
    int a(1),b(2),c(3);
    asm ("": "=r"(c):"r"(a));
    asm ("": "=r"(a):"r"(b));
    asm ("": "=r"(b):"r"(c));
    printf("%d %d %d", a,b,c);
}

Может кто-нибудь объяснить, что здесь происходит?

1 Ответ

6 голосов
/ 03 ноября 2019

В основном случайный шанс выделения переменных. (Или фактически первый выбор внутреннего механизма GCC).

Вы использовали операнды asm только для вывода "=r", но затем ваш шаблон asm фактически не записывает этот регистр, поэтому вы получаете любое значение, которое находилось взарегистрируйте GCC.

Это очень похоже на неопределенное поведение C при использовании неинициализированной переменной.

Чтобы увидеть, что произошло, поместите комментарии asm в шаблон asm, который расширяется. %0 и %1 внутри шаблона внутри комментария asm. Это будет не влиять на то, как GCC распределяет регистры: ему все равно, использует ли шаблон регистры, которые он выбирает неявно или явно;вам нужно написать полезный шаблон и сопоставить его с ограничениями операнда.

С вашим кодом в проводнике компилятора Godbolt с gcc9.2 -O3 -fverbose-asm :

.intel_syntax noprefix
.LC0:
        .string "%d %d %d"
main:
        sub     rsp, 8    #,
        mov     edi, OFFSET FLAT:.LC0     #,
        xor     eax, eax  #
        mov     ecx, 1    # tmp87,
        mov     esi, 2    # tmp89,
        nop  #ecx ecx   # c, tmp87
        nop  #esi esi   # a, tmp89
        nop  #edx ecx   # b, c
        call    printf  #
        xor     eax, eax  #
        add     rsp, 8    #,
        ret     

(Вместо простого комментария asm я помещаю комментарии к инструкциям NOP, например
asm ("nop #%0 %1": "=r"(c):"r"(a));, чтобы фильтрация компилятора и проводника не удаляла их. Шаблоны asmподстановка чистого текста перед передачей его ассемблеру, так что это все равно в точности соответствует тому, как GCC скомпилировал бы ваш исходный источник с теми же параметрами.)

В первых двух случаях gcc решил выбратьтот же регистр для вывода, что и для ввода, так что они работали как назначения.

В 3-м случае c уже был в регистре, и размещение 3 в любом месте было оптимизировано, потому что"=r"(c) перезаписать это значение до того, как оно было прочитано.

Возможно, вы компилировали с отключенной оптимизацией? Вы тоже можете сделать это и следить за тем, что произошло. (Возможно, GCC выберет eax для ввода и вывода каждый раз). Я обычно не пытаюсь смотреть на антиоптимизированный -O0 asm, потому что он полон шума хранения / перезагрузки.

...