首先,我发现了这个问题:How do I atomically read a value in x86 ASM? 但它有点不同,在我的情况下,我想在32位应用程序中原子地分配一个浮点(64位双倍)值。
来自:“英特尔®64和IA-32架构软件开发人员手册,第3A卷”
奔腾处理器(以及更新的处理器)保证以下额外的内存操作将始终以原子方式执行:
读取或写入在64位边界上对齐的四字
实际上是否可以使用一些组装技巧?
答案 0 :(得分:5)
在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
加载或存储。 e.g。
; 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,movq
来自/ mm0-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从gcc中查看asm x87,SSE和SSE2。来自clang4.0.1和ICC18。
gcc作为int-&gt; xmm或xmm-&gt; int的一部分在内存中反弹,即使SSE4(pinsrd
/ pextrd
)可用。这是一次错过优化(gcc bug 80833)。在64位模式下,它支持ALU movd + pinsrd / pextrd -mtune=intel
或-mtune=haswell
,但显然不是32位模式,不适用于此用例(XMM中的64位整数而不是适当的矢量化)。无论如何,请记住,只有来自atomic<long long> shared
的加载或存储必须是原子的,其他加载/存储到堆栈是私有的。