注意到几个问题,
(1) 主要问题是约束,“rax”并不像看起来那样,而是第一个字符“r”让 gcc 使用任何寄存器。
(2) 不确定您的存储类型如何::uint128_t,但假设 x86 平台使用标准小端,则高位和低位双字也会交换。
(3) 获取某个地址并将其转换为其他地址可能会违反别名规则。取决于您的 types::uint128_t 的定义方式,这是否是一个问题(如果它是两个 uint64_t 的结构则很好)。假设不违反别名规则,带有 -O2 的 GCC 将进行优化。
(4) *src 实际上应该标记为输出,而不是指定内存破坏器。但这实际上更多的是性能问题而不是正确性问题。类似地,rbx 和 rcx 不需要指定为 clobbered。
这是一个有效的版本,
#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 );
}