gcc, __atomic_exchange 似乎产生非原子汇编,为什么?
gcc, __atomic_exchange seems to produce non-atomic asm, why?
我正在开发一个不错的工具,它需要 atomic 交换两个不同的 64 位值。在 amd64 架构上,可以使用 XCHGQ
指令(参见 doc 中的此处,警告:这是一个很长的 pdf)。
相应地,gcc 有一些原子内置函数,理想情况下它们可以做同样的事情,因为它是可见的,例如 here。
使用这 2 个文档,我生成了以下简单的 C 函数,用于两个 64 位值的原子交换:
void theExchange(u64* a, u64* b) {
__atomic_exchange(a, b, b, __ATOMIC_SEQ_CST);
};
(顺便说一句,我不是很清楚,为什么需要 "atomic exchange" 3 个操作数。)
对我来说有点可疑,gcc __atomic_exchange
宏使用了 3 个操作数,所以我测试了它的 asm 输出。我用 gcc -O6 -masm=intel -S
编译了它,得到了以下输出:
.LHOTB0:
.p2align 4,,15
.globl theExchange
.type theExchange, @function
theExchange:
.LFB16:
.cfi_startproc
mov rax, QWORD PTR [rsi]
xchg rax, QWORD PTR [rdi] /* WTF? */
mov QWORD PTR [rsi], rax
ret
.cfi_endproc
.LFE16:
.size theExchange, .-theExchange
.section .text.unlikely
正如我们所见,结果函数不仅包含单个数据移动,还包含三个不同的数据移动。因此,正如我对这段 asm 代码的理解,这个函数并不是真正的原子函数。
怎么可能?也许我误解了一些文档?我承认,gcc 内置文档对我来说不是很清楚。
这是 __atomic_exchange_n (type *ptr, type val, int memorder)
的通用版本,其中只有 ptr
上的交换操作是原子的,val
的读取不是。在通用版本中,val
是通过指针访问的,但原子性仍然不适用于它。指针是这样的,当编译器必须调用外部帮助程序时,它可以处理多种大小:
The four non-arithmetic functions (load, store, exchange, and
compare_exchange) all have a generic version as well. This generic
version works on any data type. It uses the lock-free built-in
function if the specific data type size makes that possible;
otherwise, an external call is left to be resolved at run time. This
external call is the same format with the addition of a ‘size_t’
parameter inserted as the first parameter indicating the size of the
object being pointed to. All objects must be the same size.
我正在开发一个不错的工具,它需要 atomic 交换两个不同的 64 位值。在 amd64 架构上,可以使用 XCHGQ
指令(参见 doc 中的此处,警告:这是一个很长的 pdf)。
相应地,gcc 有一些原子内置函数,理想情况下它们可以做同样的事情,因为它是可见的,例如 here。
使用这 2 个文档,我生成了以下简单的 C 函数,用于两个 64 位值的原子交换:
void theExchange(u64* a, u64* b) {
__atomic_exchange(a, b, b, __ATOMIC_SEQ_CST);
};
(顺便说一句,我不是很清楚,为什么需要 "atomic exchange" 3 个操作数。)
对我来说有点可疑,gcc __atomic_exchange
宏使用了 3 个操作数,所以我测试了它的 asm 输出。我用 gcc -O6 -masm=intel -S
编译了它,得到了以下输出:
.LHOTB0:
.p2align 4,,15
.globl theExchange
.type theExchange, @function
theExchange:
.LFB16:
.cfi_startproc
mov rax, QWORD PTR [rsi]
xchg rax, QWORD PTR [rdi] /* WTF? */
mov QWORD PTR [rsi], rax
ret
.cfi_endproc
.LFE16:
.size theExchange, .-theExchange
.section .text.unlikely
正如我们所见,结果函数不仅包含单个数据移动,还包含三个不同的数据移动。因此,正如我对这段 asm 代码的理解,这个函数并不是真正的原子函数。
怎么可能?也许我误解了一些文档?我承认,gcc 内置文档对我来说不是很清楚。
这是 __atomic_exchange_n (type *ptr, type val, int memorder)
的通用版本,其中只有 ptr
上的交换操作是原子的,val
的读取不是。在通用版本中,val
是通过指针访问的,但原子性仍然不适用于它。指针是这样的,当编译器必须调用外部帮助程序时,它可以处理多种大小:
The four non-arithmetic functions (load, store, exchange, and compare_exchange) all have a generic version as well. This generic version works on any data type. It uses the lock-free built-in function if the specific data type size makes that possible; otherwise, an external call is left to be resolved at run time. This external call is the same format with the addition of a ‘size_t’ parameter inserted as the first parameter indicating the size of the object being pointed to. All objects must be the same size.