x86_64上的原子16字节操作

时间:2018-03-10 21:49:16

标签: c x86-64 inline-assembly

以下16字节原子操作是否正确实现?还有更好的选择吗?

typedef struct {
    uintptr_t low;
    uintptr_t high;
} uint128_atomic;


uint128_atomic load_relaxed(uint128_atomic const *atomic)
{
    uint128_atomic ret;
    asm volatile("xor %%eax, %%eax\n"
                 "xor %%ebx, %%ebx\n"
                 "xor %%ecx, %%ecx\n"
                 "xor %%edx, %%edx\n"
                 "lock; cmpxchg16b %1"
                 : "=A"(ret)
                 : "m"(*atomic)
                 : "cc", "rbx", "rcx");
    return ret;
}

bool cmpexch_weak_relaxed(
    uint128_atomic *atomic,
    uint128_atomic *expected,
    uint128_atomic desired)
{
    bool matched;
    uint128_atomic e = *expected;
    asm volatile("lock; cmpxchg16b %1\n"
                 "setz %0"
                 : "=q"(matched), "+m"(atomic->ui)
                 : "a"(e.low), "d"(e.high), "b"(desired.low), "c"(desired.high)
                 : "cc");
    return matched;
}

void store_relaxed(uint128_atomic *atomic, uint128_atomic val)
{
    uint128_atomic old = *atomic;
    asm volatile("lock; cmpxchg16b %0"
                 : "+m"(*atomic)
                 : "a"(old.low), "d"(old.high), "b"(val.low), "c"(val.high)
                 : "cc");
}

有关完整的工作示例,请结帐:

https://godbolt.org/g/CemfSg

可在此处找到更新的实施:https://godbolt.org/g/vGNQG5

3 个答案:

答案 0 :(得分:2)

不,你需要在cmpexch_weak_relaxed和store_relaxed中使用“+ a”和“+ d”。

除此之外,我没有看到任何问题。 (我将自己在工作软件中的实现进行了比较。)

就改进而言,我建议

uint128_atomic load_relaxed(uint128_atomic const *atomic)
{
    uint128_atomic ret = { 0, 0 };
    asm volatile("lock; cmpxchg16b %1"
                 : "+A"(ret)
                 : "m"(*atomic), "b"(0), "c"(0)
                 : "cc");
    return ret;
}

(我看到David Wohlferd也在评论中提出了这个建议。)

答案 1 :(得分:2)

在应用@PeterCordes@David Wohlferd@prl的所有建议后,我提出了以下实施方案。非常感谢!

struct _uint128_atomic {
    volatile uint64_t low;
    volatile uint64_t high;
} __attribute__((aligned(16)));
typedef struct _uint128_atomic uint128_atomic;


bool
cmpexch_weak_relaxed(
    uint128_atomic *atomic,
    uint128_atomic *expected,
    uint128_atomic desired)
{
    bool matched;
    uint128_atomic e = *expected;
    asm volatile("lock cmpxchg16b %1"
                 : "=@ccz"(matched), "+m"(*atomic), "+a"(e.low), "+d"(e.high)
                 : "b"(desired.low), "c"(desired.high)
                 : "cc");
    if (!matched)
        *expected = e;
    return matched;
}


uint128_atomic
load_relaxed(uint128_atomic const *atomic)
{
    uint128_atomic ret = {0, 0};
    asm volatile("lock cmpxchg16b %1"
                 : "+A"(ret)
                 : "m"(*atomic), "b"(0), "c"(0)
                 : "cc");
    return ret;
}


void
store_relaxed(uint128_atomic *atomic, uint128_atomic val)
{
    uint128_atomic old = *atomic;
    while (!cmpexch_weak_relaxed(atomic, &old, val))
        ;
}

请注意,该实施是针对GCC的,并且不适用于clangclang中GCC内联汇编的实现最好是次优的,最糟糕的是垃圾。 GCC的实现也可以在Godbolt的Compiler Explorer here中找到。 可以找到次优但有效的clang实施here

答案 2 :(得分:1)

为什么不使用C11原子内在函数?

#include <stdatomic.h>

inline __uint128_t load_relaxed(_Atomic __uint128_t *obj)
{
  return atomic_load_explicit(obj, memory_order_relaxed);
}

inline _Bool cmpexch_weak_relaxed(_Atomic __uint128_t *obj,
                                  __uint128_t *expected,
                                  __uint128_t desired)
{
  return atomic_compare_exchange_weak_explicit(obj, expected, desired,
    memory_order_relaxed, memory_order_relaxed);
}

使用clang 4.0.1和-march=native编译或多或少编写你编写的程序集。但是,与您编写的内容不同,编译器实际上了解正在发生的事情,因此代码生成围绕这些函数将是正确的。据我所知,没有办法注释GNU样式的程序集插件,告诉编译器它具有原子操作的语义。