Заметил несколько проблем,
(1) Основная проблема заключается в ограничениях: rax не делает то, на что он похож, скорее, первый символ "r" позволяет gcc использовать любой регистр.
(2) Не знаете, как ваши типы хранения :: uint128_t, но при условии стандартного байтового порядка для платформ x86, тогда верхние и нижние мечи также меняются местами.
(3) Взятиеадрес чего-либо и приведение его к чему-то другому может нарушить правила наложения имен.Зависит от того, как ваш types :: uint128_t определен как влажный или нет, это проблема (хорошо, если это структура из двух uint64_t).GCC с -O2 оптимизирует при условии, что правила псевдонимов не нарушаются.
(4) * src действительно должен быть помечен как выход, а не задан клопбер памяти.но это действительно скорее вопрос производительности, чем правильности.Точно так же rbx и rcx не нужно указывать как закрытые.
Вот версия, которая работает,
#include <stdint.h>
namespace types
{
// alternative: union with unsigned __int128
struct uint128_t
{
uint64_t lo;
uint64_t hi;
}
__attribute__ (( __aligned__( 16 ) ));
}
template< class T > inline bool cas( volatile T * src, T cmp, T with );
template<> inline bool cas( volatile types::uint128_t * src, types::uint128_t cmp, types::uint128_t with )
{
// cmp can be by reference so the caller's value is updated on failure.
// suggestion: use __sync_bool_compare_and_swap and compile with -mcx16 instead of inline asm
bool result;
__asm__ __volatile__
(
"lock cmpxchg16b %1\n\t"
"setz %0" // on gcc6 and later, use a flag output constraint instead
: "=q" ( result )
, "+m" ( *src )
, "+d" ( cmp.hi )
, "+a" ( cmp.lo )
: "c" ( with.hi )
, "b" ( with.lo )
: "cc", "memory" // compile-time memory barrier. Omit if you want memory_order_relaxed compile-time ordering.
);
return result;
}
int main()
{
using namespace types;
uint128_t test = { 0xdecafbad, 0xfeedbeef };
uint128_t cmp = test;
uint128_t with = { 0x55555555, 0xaaaaaaaa };
return ! cas( & test, cmp, with );
}