Прежде всего, https://gcc.gnu.org/wiki/DontUseInlineAsm. Существует в основном нулевая причина для того, чтобы бросить свой собственный CAS, по сравнению с использованием bool __atomic_compare_exchange(type *ptr, type *expected, type *desired, bool weak, int success_memorder, int failure_memorder)
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html. Это работает даже с переменными, отличными от _Atomic
.
"=r"
сообщает gcc, что он может запрашивать вывод в любом регистре, который хочет, поэтому он может избежать необходимости mov
результата там . (Как здесь, где GCC хочет вывод в RSI как аргумент для printf). И / или поэтому он может избежать разрушения ввода, который он поместил в тот же регистр. В этом весь смысл =r
вместо ограничений конкретного регистра.
Если вы хотите сообщить GCC, что регистр, который он выбирает для ввода, также является регистром вывода, используйте "+r"
. Или в этом случае, поскольку вам нужно выбрать RAX, используйте "+a"(expected)
.
. Уже есть синтаксис, чтобы компилятор выбирал один и тот же регистр для 2 ограничений с отдельными переменными для ввода и вывода, в частности, для соответствия ограничениям: "=r"(outvar) : "0"(invar)
.
Оптимизация была бы пропущена, если бы синтаксис не позволил бы вам описать неразрушающую инструкцию, которая могла бы производить вывод в другом регистре из входных данных.
Вы можете увидеть, что GCC на самом деле выбрал, используя ограничение в комментарии.
Помните, что встроенный asm GNU C - это просто подстановка текста в ваш шаблон. Компилятор буквально не знает, что делают инструкции asm, и даже не проверяет их правильность. (Это происходит только тогда, когда ассемблер читает выходные данные компилятора.)
...
asm volatile (
"lock cmpxchgq %3, %1 # 0 out: %0 | 2 in: %2"
: ...
...
Результирующий asm очень ясно показывает проблему ( Godbolt GCC7.4 ):
lock cmpxchgq %rsi, (%rsp) # 0 out: %rsi | 2 in: %rax
leaq .LC0(%rip), %rdi
xorl %eax, %eax
call printf@PLT
(Я использовал синтаксис AT & T, чтобы ваш cmpxchgq %reg,mem
соответствовал mem,reg
порядку операндов , задокументированному Intel , хотя и GAS, и встроенный ассемблер clang, похоже, принимают его и в другом порядке. Также из-за суффикса размера операнда)
GCC использует возможность запросить вывод "=r"(expected)
в RSI в качестве аргумента для printf. Ваша ошибка в том, что ваш шаблон неверно полагает, что %0
расширится до rax
.
Существует множество примеров отсутствия неявной связи между входом ивывод, который случается использовать тот же C var. Например, вы можете поменять местами 2 переменные Си с помощью пустого оператора asm, просто используя ограничения. Как написать короткий блок встроенной расширенной сборки GNU для замены значений двух целочисленных переменных?