如何在x86 ASM中原子地移动64位值?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在x86 ASM中原子地移动64位值?相关的知识,希望对你有一定的参考价值。

首先,我发现了这个问题:How do I atomically read a value in x86 ASM?但它有点不同,在我的情况下,我想在32位应用程序中原子地分配一个浮点(64位双精度)值。

来自:“英特尔®64和IA-32架构软件开发人员手册,Volume3A”

奔腾处理器(以及更新的处理器)保证以下额外的内存操作将始终以原子方式执行:

读取或写入在64位边界上对齐的四字

实际上是否可以使用一些组装技巧?

答案

在64位x86 asm中,您可以使用整数mov rax, [rsi],或x87或SSE2。 As long as the address is 8-byte aligned (or on Intel p6 and later CPUs: doesn't cross a cache-line boundary) the load or store will be atomic


在32位x86 asm中,只使用整数寄存器的唯一选择是lock cmpxchg8b,但这对于纯负载或纯存储来说很糟糕。 (您可以通过设置expected = desired = 0将其用作加载,但只读内存除外)。 (gcc / clang在64位模式下使用lock cmpxchg16b用于atomic<struct_16_bytes>,但是一些编译器只是选择使16字节对象不是无锁的。)

所以答案是:不要使用整数寄存器:fild qword / fistp qword可以复制任何位模式而无需更改它。 (只要x87精度控制设置为完整的64位尾数)。对于Pentium及更高版本的对齐地址,这是原子的。

在现代x86上,使用SSE2 movq加载或存储。例如

; atomically store edx:eax to qword [edi], assuming [edi] is 8-byte aligned
movd   xmm0, eax
pinsrd xmm0, edx            ; SSE4.1
movq   [edi], xmm0

只有SSE1,请使用movlps。 (对于加载,您可能希望使用xorps打破对xmm寄存器的旧值的错误依赖性)。

使用MMX,来自movqmm0-7可以使用。


gcc在32位模式下以movq的优先顺序使用SSE2 movlps,SSE1 fild或x87 fstp / std::atomic<int64_t>。不幸的是,Clang -m32使用lock cmpxchg8b,即使SSE2可用:LLVM bug 33109。 。

某些版本的gcc配置为使-msse2默认启用,即使使用-m32(在这种情况下,您可以使用-mno-sse2-march=i486来查看gcc没有它的情况)。

我把load and store functions on the Godbolt compiler explorer用x87,SSE和SSE2来看gm。来自clang4.0.1和ICC18。

gcc作为int-> xmm或xmm-> int的一部分在内存中反弹,即使SSE4(pinsrd / pextrd)可用。这是一个错过优化(gcc bug 80833)。在64位模式下,它有利于ALU movd + pinsrd / pextrd与-mtune=intel-mtune=haswell,但显然不是32位模式或不用于此用例(XMM中的64位整数而不是正确的矢量化)。无论如何,请记住,只有来自atomic<long long> shared的加载或存储必须是原子的,其他加载/存储到堆栈是私有的。

以上是关于如何在x86 ASM中原子地移动64位值?的主要内容,如果未能解决你的问题,请参考以下文章

asm x86中的变量声明顺序?

GCC 生成的 ASM 简化了 x86 ASM?如何映射?

ASM:MASM、NASM、FASM?

x86 asm 指令集:任何 _searchable_ 离线参考?

x86平台转x64平台关于内联汇编不再支持的解决

在 c++ 中使用 x86 DIV 的这个 asm 块有啥用?