В основном случайный шанс выделения переменных. (Или фактически первый выбор внутреннего механизма 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, потому что он полон шума хранения / перезагрузки.